Gestione di costruttori con molti parametri in Java


105

In alcuni dei nostri progetti, c'è una gerarchia di classi che aggiunge più parametri man mano che scende la catena. In fondo, alcune delle classi possono avere fino a 30 parametri, 28 dei quali vengono passati al super costruttore.

Riconosco che l'uso di DI automatizzato attraverso qualcosa come Guice sarebbe bello, ma a causa di alcuni motivi tecnici, questi progetti specifici sono limitati a Java.

Una convenzione di organizzare gli argomenti in ordine alfabetico per tipo non funziona perché se un tipo viene riformattato (il cerchio che stavi passando per l'argomento 2 ora è una forma) può improvvisamente essere fuori uso.

Questa domanda potrebbe essere troppo specifica e irta di critiche "Se questo è il tuo problema, stai sbagliando a livello di progettazione", ma cerco solo punti di vista.

Risposte:


264

Il Builder Design Pattern potrebbe aiutare. Considera il seguente esempio

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

Questo ci consente di scrivere codice come

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

Se tralasciamo un campo obbligatorio (presumibilmente il nome è obbligatorio), allora possiamo fare in modo che il costruttore di Student lanci un'eccezione. E ci consente di avere argomenti predefiniti / opzionali senza dover tenere traccia di alcun tipo di ordine degli argomenti, poiché qualsiasi ordine di quelle chiamate funzionerà ugualmente bene.


10
Ovviamente con le importazioni statiche non devi nemmeno "vedere" questi "builder". Ad esempio, potresti avere il nome di metodi statici (nome stringa) che restituisce un generatore e Student (StudentBuilder) che restituisce uno studente. Da qui Student (nome ("Joe"). Age (15) .motto ("Mi sono bagnato"));
oxbow_lakes

2
@oxbow_lakes: nel tuo esempio, quale classe ha il nome del metodo statico (nome stringa)?
user443854

Tecnicamente parlando, è possibile utilizzare la classe Student per costruire un nuovo studente. Ho aggiunto i metodi all'interno della classe Student e ha funzionato perfettamente. In questo modo non avevo bisogno di un'altra classe di builder. Non sono sicuro che ciò sia desiderabile però. C'è un motivo per utilizzare un'altra classe (StudentBuilder) per crearlo?
WVrock

1
@WVrock: dipende dalla tua implementazione. Come ho detto nella mia risposta, farlo con la stessa classe studentesca potrebbe potenzialmente lasciare la classe in uno stato semi-inizializzato, ad esempio se si dispone di un campo obbligatorio che non è stato ancora inizializzato.
Eli Courtwright

@EliCourtwright Immagino che si tratti di preferenza / progettazione del codice. Invece di fare in modo che il costruttore generi l'eccezione, ho fatto in modo che il buildStudent()metodo lanci l'eccezione.
WVrock

24

Puoi incapsulare i parametri correlati all'interno di un oggetto?

ad esempio, se i parametri sono simili


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

allora potresti invece avere:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}



8

Bene, l'utilizzo del pattern builder potrebbe essere una soluzione.

Ma una volta che si arriva a 20-30 parametri, immagino che ci sia una relazione elevata tra i parametri. Quindi (come suggerito) avvolgerli in oggetti dati logicamente sani probabilmente ha più senso. In questo modo l'oggetto dati può già verificare la validità dei vincoli tra i parametri.

Per tutti i miei progetti in passato, una volta arrivato al punto di avere troppi parametri (e quello era 8 e non 28!) Sono stato in grado di disinfettare il codice creando un datamodel migliore.


4

Dato che sei vincolato a Java 1.4, se vuoi DI allora Spring sarebbe un'opzione molto decente. DI è utile solo in luoghi in cui i parametri del costruttore sono servizi o qualcosa che non varia durante il runtime.

Se hai tutti questi diversi costruttori dovuti al fatto che desideri opzioni variabili su come costruire un oggetto, dovresti prendere seriamente in considerazione l'utilizzo del modello Builder.


I parametri sono per lo più servizi come hai detto, quindi DI è ciò di cui avrei bisogno. Penso che il modello Builder menzionato in un paio di altre risposte sia esattamente quello che speravo.
Steve Armstrong

4

La soluzione migliore è non avere troppi parametri nel costruttore. Solo i parametri realmente necessari nel costruttore sono i parametri necessari per inizializzare correttamente l'oggetto. Puoi avere costruttori con più parametri, ma anche avere un costruttore con solo i parametri minimi. I costruttori aggiuntivi chiamano questo semplice costruttore e successivamente i setter per impostare gli altri parametri. In questo modo puoi evitare il problema della catena con sempre più parametri, ma hai anche alcuni costruttori di convenienza.



1

Il refactoring per ridurre il numero di parametri e la profondità della gerarchia di ereditarietà è praticamente tutto ciò a cui riesco a pensare perché, nulla aiuterà davvero a mantenere dritti 20 parametri. Dovrai solo fare ogni singola chiamata mentre guardi la documentazione.

Una cosa che potresti fare è raggruppare alcuni parametri raggruppati in modo logico nel loro oggetto di livello superiore, ma questo ha i suoi problemi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.