Perché il modello di builder è spesso implementato in questo modo?

7

Spesso vedo l'implementazione del modello di builder (in Java) come segue:

public class Foo {
    private Foo(FooBuilder builder) {
        // get alle the parameters from the builder and apply them to this instance
    }

    public static class FooBuilder {
        // ...
        public Foo build() {
            return new Foo(this); // <- this part is what irritates me
        }
    }
}

Esempi anche qui:

Perché le persone introducono la strong (reciproca) dipendenza tra builder e type per costruire?

EDIT: Voglio dire, so che per sua natura il costruttore è legato alla classe da cui voglio creare un'istanza ( FooBuilder -> Foo ). Quello che chiedo è: perché c'è la necessità del contrario ( Foo -> FooBuilder )

Al contrario, vedo implementazioni (meno frequenti) che creano una nuova istanza di Foo quando viene creato il builder e impostano i campi sull'istanza di Foo quando vengono chiamati i metodi builder appropriati. Mi piace questo approccio, perché ha ancora un'API fluente e non lega la classe al suo costruttore. C'è qualche aspetto negativo di questo altro approccio?

    
posta Andy 28.09.2016 - 10:40
fonte

3 risposte

7

Si riduce a mai che ha un Foo in uno stato semi-costruito

Alcuni motivi mi vengono in mente:

Il costruttore è per sua natura legato alla classe che sta costruendo. Puoi pensare agli stati intermedi del builder come parziale applicazione del costruttore, quindi il tuo argomento che è troppo accoppiato non è convincente.

Passando l'intero builder in, i campi in MyClass possono essere definitivi, rendendo più facile ragionare su MyClass. Se i campi non sono definitivi, potresti facilmente avere setter fluenti rispetto a un costruttore.

    
risposta data 28.09.2016 - 11:27
fonte
3

Il costruttore manterrà campi mutabili, impostati con metodi. La classe attuale ( Foo ) può quindi creare campi final immutabili dal builder.

Quindi il Builder è una classe stateful.

Ma il costruttore potrebbe aver chiamato new Foo(x, y, z) . Questo sarebbe più IMHO one-directional, better style. Come allora l'anti-pattern, un campo FooBuilder o simili non sarebbe possibile. D'altra parte il FooBuilder garantisce un'integrità dei dati.

D'altro canto FooBuilder svolge due ruoli: per costruire con un'API fluente e per presentare i dati al costruttore. È una classe di tipo evento per il costruttore. In un codice sovrastampato i parametri del costruttore potrebbero essere conservati in alcuni FooConstructorParams.

    
risposta data 28.09.2016 - 12:27
fonte
2

Perché un "Builder" viene tipicamente usato per risolvere il "problema del costruttore telescopico", specialmente nel contesto di classi immutabili. Vedi questo link per un esempio a pieno titolo.

Considera quali sarebbero le alternative:

 public static class FooBuilder {
    // works with immutable classes, but leads to telescoping constructor
    public Foo build() {
        return new Foo(par1, par2, par3, ....); 
    }
 }

o

 public static class FooBuilder {
    // does not work for immutable classes:
    public Foo build() {
        var foo = new Foo(); 
        foo.Attribute1=par1;
        foo.Attribute2=par2;
        //...
        return foo;
    }
 }

Quindi, specialmente per le classi immutabili, se non vuoi un costruttore con un gruppo di parametri, in realtà non esiste un'alternativa.

    
risposta data 28.09.2016 - 12:07
fonte

Leggi altre domande sui tag