Ci sono alcune opzioni per come la vedo io:
Opzione 1: passa il Builder Around
Non c'è alcun vantaggio nel costruire e restituire effettivamente un oggetto che dovrà essere cambiato. Basta passare l'oggetto Builder
dove mai deve andare e fornire l'accesso ai suoi campi in modo che tutto ciò che deve accadere tra ogni fase di costruzione possa conoscere lo stato corrente del builder. Gli svantaggi di questo sono che non puoi "bloccare" una parte dello stato Builders
se è qualcosa che devi fare. Può essere fatto tecnicamente con una funzione lockInStage1()
o qualcosa che cambia un flag che impedisce l'utilizzo di alcuni setter, ma è un po 'disordinato.
Opzione 2: fornire un'implementazione ReadOnly
Questo è fondamentalmente la stessa cosa dell'opzione 1, ma senza l'oggetto Builder
. Basta avere una versione mutabile dell'oggetto e quindi consentire all'utente di creare una versione immutabile da esso.
Ad esempio:
public class ReadOnlyMyObject {
private int field1;
private int field2;
public ReadOnlyMyObject(MyObject obj) {
field1 = obj.field1;
field2 = obj.field2;
}
public int getField1() {
return field1;
}
}
public class MyObject {
public int field1;
public int field2;
//fields can either be public or accessed through getters and setters
public ReadOnlyMyObject asReadOnly() {
return new ReadOnlyMyObject(this);
}
}
Personalmente, preferisco questo sul modello del builder in quanto ti lascia aperto ad avere un oggetto mutabile se è mai necessario, e perché avere una classe Builder
se l'oggetto non viene costruito come un pezzo unico. Tuttavia ha lo stesso svantaggio dell'opzione 1. Non puoi realmente bloccare lo stato di ogni fase.
Opzione 3: Builder per ogni stage
Invece di un singolo oggetto e Builder
controparte, devi semplicemente creare un oggetto per ogni fase e costruirlo individualmente. Il Builder
per ogni fase assumerebbe quindi come parametro la fase precedente.
public class Stage1 {
//stage 1 fields
public class Builder {
//Builder fields
public Builder() { }
//stage 1 builder functions
public Stage1 build() {
//build stage 1
}
}
}
public class Stage2 {
private Stage1 stage1
//stage 2 fields
public class Builder {
private Stage1 stage1;
//Builder fields
public Builder(Stage1 stage1) {
this.stage1 = stage1;
}
//stage 2 builder functions
public Stage1 build() {
Stage2 stage2 = new Stage2(this.stage1);
//build stage 2
}
}
}
public class FinalObject {
private Stage2 stage2
//fields
public class Builder {
private Stage2 stage2;
//Builder fields
public Builder(Stage2 stage2) {
this.stage2 = stage2;
}
//builder functions
public FinalObject build() {
FinalObject obj = new FinalObject(this.stage2);
//build final object
}
}
}
Non amo questo, perché è potenzialmente necessario creare molte classi. Tuttavia ci sono alcuni reali vantaggi per questo metodo.
-
Ogni fase è bloccata. Una volta creata, non può essere modificata.
-
Se è necessario aggiungere fasi tra 2 stadi esistenti è abbastanza semplice (un po 'come l'aggiunta di un oggetto a LinkedList
ha un overhead molto piccolo. Per questo motivo gli oggetti stage non dovrebbero essere chiamati Stage
, Stage2
o qualsiasi valore ordinale in quanto ti costringerebbe a rinominarli tutti.
Sommario
In generale, direi che se le fasi devono essere bloccate, vai con i Multiple Builder se non hai bisogno di modificare i dati in una fase precedente una volta creata. Può essere fatto, ma combatte il modello.
Se i dati di ogni fase possono essere potenzialmente modificati da ciò che accade nelle fasi successive, passa con il passaggio di Builder
o fornisci una versione dell'oggetto ReadOnly.