2012-09-19 10 views
9

Sono un web dev (sviluppatore di giochi per hobby) e mi sono visto usare più volte il seguente paradigma. (Sia nello sviluppo dell'architettura del server che nel lavoro con i videogiochi.) Sembra davvero brutto, ma non conosco un lavoro. Darò un esempio in game dev, perché è dove l'ho notato di recente. Questo è un gioco di ruolo su cui ho lavorato. Ogni volta che viene lanciata una battaglia, CombatEngine crea due partiti di Combattenti. Ogni Combatant imposta un oggetto ArtificialIntelligence che è associato con il combattente dato, che ha il compito di dettare mosse per i giocatori che non ricevono un comando esplicito:Strani riferimenti di passaggio in classe

public class Combatant { 

    ArtificialIntelligence ai = null; 

    public Combatant() 
    { 
     // Set other fields here. 

     this.ai = new ArtificialIntelligence(this); 
    } 

} 

Ecco cosa non mi piace: il campo interno (ArtificialIntelligence) prende un Combatant durante la costruzione, perché ha bisogno di alcuni campi Combatant per dettare le azioni appropriate. Quindi, per comodità, tengo un riferimento al combattente passato come argomento all'oggetto ArtificialIntelligence, ma quell'oggetto contiene un riferimento all'oggetto ai stesso! Crea questa ricorsione strana, ma non so come aggirarla. L'oggetto AI ha bisogno di molti campi specifici per il combattente, ecco perché ho passato l'intero oggetto, ma non mi piace come l'oggetto contenga il riferimento al campo ai che è contenuto nel combattente soprastante campo, che è contenuto nella classe Ai sovrastante. Questa cattiva pratica o sto semplicemente pensando?

+6

Stai riscontrando un errore di stackoverflow? Ne dubito, e se no, non c'è nessuna ricorsione in corso qui, solo riferimento di passaggio. Penso che tu abbia un non-problema qui. –

+1

Woops, sono un matematico di mestiere, quindi manca una parte della mia terminologia. Hai ragione, questo è solo un passaggio di riferimento. Sarebbe ancora considerato un problema? Non è una cattiva pratica nidificare i riferimenti in questa forma? E per rispondere alla tua domanda, non ricevo errori. Ho solo pensato che fosse brutto e volevo avere delle opinioni. – Sal

risposta

9

Anche se non c'è nessun problema "design" qui - è solo un punto di riferimento si sta passando - una considerazione importante è che si dovrebbe inizializzare tutti i campi prima al passaggio this per l'altra classe. In caso contrario, l'altra classe avrà accesso a this in uno stato potenzialmente incoerente. Questo è talvolta chiamato lasciando "escape" this dal costruttore.

Non fare questo ...

public class BadCombatant { 

    ArtificialIntelligence ai = null; 
    String someField; 

    public BadCombatant() { 
     this.ai = new ArtificialIntelligence(this); 
     // Don't do this - ArtificialIntelligence constructor saw someField as null 
     someField = "something"; 
    } 
+1

+1 Alcuni IDE avvertiranno di una "perdita di questo" nei costruttori. –

+1

Per essere chiari, il costruttore * di ArtificialIntelligence' (e tutti i metodi che chiama) vedranno 'someField' come' null'. –

+1

Non penso sia una buona idea mantenere questa dipendenza ciclica. Guarda la mia risposta! Non è necessario che le due classi dipendano parzialmente l'una dall'altra quando questa dipendenza può essere spostata su una delle due classi o su una classe completamente nuova. – CKing

4

avrei sicuramente evitare la dipendenza ciclica. Nel principio di responsabilità singola al salvataggio. È possibile eliminare la necessità di disporre di un riferimento a un'Artificialntelligence in Combattente lasciando che ArtificialIntelligence funzioni su un Combattente. Spostare invece tutto il codice da Combattente che dipende da ArtificialIntelligence per ArtificialIntelligence. Il CombatEngine farà il seguente:

  1. creare un'istanza indipendente combattente che non ha nulla a che fare con Artificialntelligence.

  2. Creare l'istanza appropriata di Artificalintelligence e passarla al Combatant precedentemente creato.

In alternativa, è possibile creare una nuova classe chiamata CombatController che viene passato un combattente e un ArtificialIntelligence. Il CombatEngine farà il seguente:

  1. Creare un combattente senza dipendenze su qualsiasi altra classe

  2. Crea un Artificialntelligence senza dipendenza da qualsiasi altra classe

  3. Creare un CombatController e passarlo gli oggetti Combatant e ArtificialIntelligence da utilizzare. Il CombatController dovrebbe esporre i metodi per controllare il Combattente e gestire il comportamento dell'IA.

Indipendentemente dall'approccio utilizzato in precedenza, si elimina la dipendenza ciclica che ti disturba.

Mi dispiace di non poter fornire un esempio di codice mentre sto digitando questa risposta dal mio telefono cellulare e la formattazione è un problema.

+0

+1 Ci sono naturalmente ancora più opzioni; potresti ad esempio estrarre il campo da Combattente quale ArtificialIntelligence ha bisogno di una nuova classe, ad esempio CombatantState. CombatantState sarebbe un campo in Combatant e ArtificialIntelligence potrebbe accettare un CombatantState. La soluzione migliore dipende dalla necessità del tuo codice. – sleske

+0

Sono assolutamente d'accordo. Quale approccio usare può essere chiaro solo quando si ha l'intera immagine e non solo una parte del problema. In entrambi i modi, la dipendenza ciclica può essere facilmente evitata! – CKing

Problemi correlati