È una cattiva pratica usare un oggetto come unico campo del Builder invece di imitare i campi della classe?

4

Lo vedo a volte:

class SomeClass {
  Object param1, param2, param3, param4;

  private SomeClass(){}      

  static class Builder {
    SomeClass someClassInstance = new SomeClass();

    // standard builder code
  }
}

VS this:

class SomeClass {
  Object param1, param2, param3, param4;

  private SomeClass(){}      

  static class Builder {
    Object param1, param2, param3, param4;

    // standard builder code
  }
}

Preferisco la prima versione, ma ho visto molto codice usando la seconda versione. SESE mi tormenta per i dettagli non abbastanza, ma ... la domanda è così semplice, così dispiaciuta per il testo inutile. : /

    
posta user932178 17.08.2017 - 21:15
fonte

3 risposte

8

Parte del motivo principale per usare un Builder, nella mia mente, è che ti permette di rendere definitive le variabili membro senza richiedere che ogni creazione passi in quei parametri a un costruttore o un metodo factory tutto in una volta.

Il tuo esempio mostra le variabili dei membri come non-finali e protette dal pacchetto. È preferibile rendere definitive e private le variabili. Nel mio codice, faccio sempre tutti i membri privati e li renderò definitivi a meno che non vi sia una specifica necessità per loro di cambiare. Poi, quando guardo la classe, so subito che queste variabili non definitive sono speciali e dove probabilmente saranno i miei problemi.

Ma in realtà non uso Builder. Il fatto che la creazione di un singolo oggetto sia così complicata che abbiamo bisogno di un'altra classe per crearne delle istanze è un odore, IMO. Non intendo dire che non ci sono ragioni per gestire la creazione di istanze. Ad esempio, potresti voler riutilizzare le istanze. Effettua le pulizie con una risorsa esterna o la sincronizzazione dei thread. Ma se semplicemente ci sono troppi parametri e / o interagiscono l'uno con l'altro in modi complessi, il mio primo pensiero è che ci sia troppo da fare nel costruttore. La prima cosa da considerare è se la composizione consente di raggruppare le cose in modo più logico.

Mi piace il concetto di interfaccia fluente che sembra fluire da questa idea. Non sono solo un fan del suo utilizzo per aggirare il fatto che ci sono troppi parametri stipati in un singolo costruttore.

    
risposta data 17.08.2017 - 22:14
fonte
3

Presumo che tu stia parlando del modello di build di Bloch , non del GoF costruttore. Il suo scopo è di evitare il problema del "costruttore telescopico", specialmente per gli oggetti immutabili . Applicato al tuo esempio, questo porta a

class SomeClass {
  private final Object param1, param2, param3, param4;

  private SomeClass(Builder builder){
      param1=builder.param1;
      param2=builder.param2;
      param3=builder.param3;
      param4=builder.param4;
  }      

  static class Builder {
    Object param1, param2, param3, param4;
    public Builder(...){
         // complex code to initialize param1 to param4
    }

    public SomeClass build() {
         return new SomeClass(this);
    }
  }
}

Quindi, come vedete ora, la vostra prima variante semplicemente non funzionerà in questo modo, dal momento che non potete creare un'istanza della classe immutabile SomeClass come sostituzione delle variabili di parametro mutabili. Se la classe SomeClass non è immutabile, tuttavia è discutibile se è necessaria una classe Builder separata.

    
risposta data 18.08.2017 - 13:24
fonte
1

Come si confrontano i due approcci?

Intuitivamente, il primo approccio sembra logico: si crea un oggetto vuoto, quindi si riempiono i suoi campi usando metodi appropriati per invocare i setter appropriati e alla fine si restituisce l'oggetto finale.

Ma come sottolineato da Robert Harvey nel suo commento: qual è il vantaggio allora? Perché utilizzare un builder e non creare direttamente l'oggetto e riempire i suoi campi con i metodi appropriati?

Esistono tuttavia casi in cui questo approccio non funziona. Ad esempio, se il tuo complesso oggetto SomeClass ha solo costruttori che richiedono parametri che devono essere prima creati. In questo caso, è necessario prima creare i parametri del costruttore e quindi, alla fine, richiamare il costruttore. Qui, solo il secondo approccio può essere previsto.

In effetti questo secondo approccio sembra essere una tecnica popolare quando un costruttore ha troppi parametri .

Qual è lo scopo del modello di progettazione del builder?

In base al GoF , l'intento del modello di builder è:

separate the construction of a complex object from its representation, so that the same construction process can create different representations.

L'idea è di costruire un oggetto passo dopo passo con:

  • un'interfaccia builder astratta per la creazione delle parti,
  • costruttori di calcestruzzo che implementano questa interfaccia passo passo per rappresentazioni specifiche e offrono una funzione per assemblare e ottenere il prodotto finale,
  • un director che ottiene il builder concreto per iniezione e lo invoca per creare le parti come richiesto.

In che modo il tuo builder si relaziona al modello di progettazione di Builder?

Entrambi gli approcci (e specialmente il secondo) sono costruttori, nel senso che costruiscono oggetti. Il secondo è leggermente più flessibile.

Tuttavia, nessuno di questi è builder secondo il GoF: qui non c'è astrazione del builder che possa dettare un'interfaccia di costruzione standardizzata su più classi che obbediscono alla stessa logica di costruzione. Mi chiedo quindi se valga davvero la complessità aggiunta, specialmente se il costruttore non accetta argomenti e i diversi oggetti previsti non sono troppo complessi da costruire.

    
risposta data 18.08.2017 - 00:20
fonte

Leggi altre domande sui tag