Come si può decomporre un costruttore?

21

Diciamo che ho una classe Enemy, e il costruttore sarebbe simile a qualcosa:

public Enemy(String name, float width, float height, Vector2 position, 
             float speed, int maxHp, int attackDamage, int defense... etc.){}

Questo sembra male perché il costruttore ha tanti parametri, ma quando creo un'istanza di Enemy ho bisogno di specificare tutte queste cose. Voglio anche questi attributi nella classe Enemy, in modo che io possa scorrere un elenco di essi e ottenere / impostare questi parametri. Stavo pensando di creare sottoclassi di Enemy in EnemyB, EnemyA, mentre codificavo il loro maxHp e altri attributi specifici, ma poi avrei perso l'accesso ai loro attributi hardcoded se avessi voluto scorrere un elenco di Enemy (composto da EnemyA, EnemyB e, di EnemyC).

Sto solo cercando di imparare a codificare in modo pulito. Se fa la differenza, lavoro in Java / C ++ / C #. Qualsiasi punto nella giusta direzione è apprezzato.

    
posta Travis 10.03.2014 - 19:51
fonte

5 risposte

59

La soluzione è raggruppare i parametri in tipi compositi. Larghezza e Altezza sono concettualmente correlate - specificano le dimensioni del nemico e di solito saranno necessarie insieme. Potrebbero essere sostituiti con un tipo Dimensions o forse un tipo Rectangle che include anche la posizione. D'altra parte, potrebbe avere più senso raggruppare position e speed in un tipo MovementData , specialmente se l'accelerazione successivamente entra nell'immagine. Dal contesto presumo che maxHp , attackDamage , defense , ecc appartengano insieme in un tipo Stats . Quindi, una firma modificata potrebbe essere simile a questa:

public Enemy(String name, Dimensions dimensions, MovementData movementData, Stats stats)

I dettagli precisi su dove disegnare le linee dipenderanno dal resto del codice e da quali dati vengono comunemente usati insieme.

    
risposta data 10.03.2014 - 20:04
fonte
24

Potresti dare un'occhiata al pattern Builder . Dal link (con esempi del modello rispetto alle alternative):

[The] Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if most of those parameters are optional. Client code is much easier to read and write with builders than with the traditional telescoping constructor pattern, and builders are much safer than JavaBeans.

    
risposta data 10.03.2014 - 20:04
fonte
5

Utilizzare sottoclassi per preimpostare alcuni valori non è desiderabile. Solo sottoclasse quando un nuovo tipo di nemico ha un comportamento diverso o nuovi attributi.

Il modello di fabbrica viene in genere utilizzato per astrarre l'esatta classe utilizzata, ma può anche essere utilizzato per fornire modelli per la creazione dell'oggetto:

class EnemyFactory {

    // each of these methods is essentially a template for a kind of enemy

    Enemy enemyA(String name, ...) {
        return new Enemy(name, ..., presetValue, ...);
    }

    Enemy enemyB(String name, ...) {
        return new Enemy(name, ..., otherValue, ...);
    }

    Enemy enemyC(String name, ...) {
        return new EnemySubclass(name, ..., otherValue, ...);
    }

    ...
}

EnemyFactory factory = new EnemyFactory();
Enemy a = factory.enemyA("fred", ...);
Enemy b = factory.enemyB("willy", ...);
    
risposta data 10.03.2014 - 20:14
fonte
0

Vorrei riservare la sotto-classe alle classi che rappresentano l'oggetto che potresti voler usare in modo indipendente, ad es. classe di personaggi in cui tutti i personaggi, non solo i nemici hanno nome, velocità, maxHp o una classe per rappresentare sprite che hanno una presenza sullo schermo con larghezza, altezza, posizione.

Non vedo nulla di intrinsecamente sbagliato con un costruttore con molti parametri di input, ma se vuoi dividerlo un po 'allora potresti avere un costruttore che imposta la maggior parte dei parametri e un altro costruttore (sovraccarico) che può essere usato per impostare quelli specifici e avere altri impostati su valori predefiniti.

A seconda della lingua che scegli di utilizzare, alcuni possono impostare valori predefiniti per i parametri di input del tuo costruttore come:

Enemy(float height = 42, float width = 42);
    
risposta data 10.03.2014 - 20:03
fonte
0

Un esempio di codice da aggiungere alla risposta di Rory Hunter (in Java):

public class Enemy{
   private String name;
   private float width;
   ...

   public static class Builder{
       private Enemy instance;

       public Builder(){
           this.instance = new Enemy();
       }


       public Builder withName(String name){
           instance.name = name;
           return this;
       }

       ...

       public Enemy build(){
           return instance;
       }
   }
}

Ora puoi creare nuove istanze di Enemy in questo modo:

Enemy myEnemy = new Enemy.Builder().withName("John").withX(x).build();
    
risposta data 11.03.2014 - 15:35
fonte

Leggi altre domande sui tag