Builder con costruttore o metodo di produzione?

2

Diciamo che ho una classe Dot con un costruttore:

public class Dot {
    private final Double x;
    private final Double y;
    private final Color color;

    private Dot(Double x, Double y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public static Builder createBuilder() {
        return new Builder();
    }

    public Double getX() { return x; }

    public Double getY() { return y; }

    public Color getColor() { return color; }

    public Builder getBuilder() {
        return new Builder().setX(x).setY(y).setColor(color);
    }

    public static class Builder {
        private Double x;
        private Double y;
        private Color color;

        public Builder setX(Double x) {
            this.x = x;
            return this;
        }

        public Builder setY(Double y) {
            this.y = y;
            return this;
        }

        public Builder setColor(Color color) {
            this.color = color;
            return this;
        }

        public Dot build() {
            return new Dot(x, y, color);
        }
    }
}

Ora vedo due possibili modi per istanziare il costruttore. Attraverso il suo costruttore:

new Dot.Builder().setX(...

o tramite un metodo factory:

Dot.createBuilder().setX(...

(In quest'ultimo caso, renderei il Builder del costruttore private .)

Metodi di fabbrica (o fabbriche) sono (a mia conoscenza) utili quando il tipo effettivo del valore restituito potrebbe cambiare in seguito. Questo è qualcosa che probabilmente non si verificherà con un builder.

Il mio argomento migliore per usare un metodo factory potrebbe essere: Dì che il builder usa un campo finale String id che deve essere impostato nel suo costruttore, ma certe restrizioni si applicano a ciò che costituisce un ID. Lanciare un'eccezione su una stringa di ID illegale non dovrebbe essere fatto in un costruttore . Quindi un metodo factory sarebbe meglio.

Tuttavia, mi sembra di aver perso alcuni argomenti più importanti. Esiste una buona pratica per questo caso? Quali altri argomenti esistono per / contro l'utilizzo di un metodo di fabbrica qui?

    
posta nfs 13.12.2017 - 08:01
fonte

1 risposta

2

Builder è una classe interna a Dot . Per me questo mi dice che Builder è strongmente accoppiato con Dot e che Builder è un'utilità di Dot per alcuni obiettivi, in questo caso costruzione di oggetti.

Questo è oggettivamente ad un passo dal fatto che Builder è una classe interna nascosta completamente dal chiamante. L'unico link che tu, il chiamante, dovresti avere a Builder dovrebbe passare per Dot come regola generale. E anche se non c'è nulla che ti impedisca di rendere pubblico il costruttore di Builder e di accedervi direttamente, credo con certezza che sia un antipattern.

Pertanto, dovresti avere un metodo factory statico in Dot che restituisce un'istanza di Builder . Hai detto che non è probabile che tu abbia mai bisogno di estendere Builder , e va tutto bene e bene, ma quando ti viene data un'opzione più flessibile senza inconvenienti mentre stai sviluppando, tieni premuto è come se la tua vita dipendesse da ciò. Se dovessi aver bisogno di estendere Builder , potresti creare Builder<T> un'interfaccia e creare un DotBuilder<Dot> personalizzato che lo implementa che, quando chiamato, crea un'istanza di Dot .

My best argument for using a factory method would be: Say that the builder uses a final field String id that has to be set in its constructor, but certain restrictions apply to what constitutes an ID. Throwing an exception on an illegal id string should not be done in a constructor. Therefore a factory method would be better.

Di solito sono contro i costruttori che eseguono lavori che potrebbero generare eccezioni. Una cosa è lanciare un'eccezione in un costruttore per un parametro non valido o per una situazione molto improbabile come un'implementazione Xml non esistente e un'altra per tentare di stabilire una connessione al database che può fallire facilmente. Se hai avuto bisogno di lavorare con il tuo costruttore di Builder , potresti farlo pigramente rimandandolo fino alla creazione effettiva dell'oggetto per non far esplodere il tuo costruttore. Anche se ho ottenuto correttamente la classe Builder pegged, non è il tipo di classe che dovrebbe mai esplodere alla creazione. Se questo non è il tuo caso, pensa seriamente a farlo funzionare nel modo in cui ci si potrebbe aspettare che funzioni (ovvero possibili eccezioni quando si chiama solo il metodo build() ).

Inoltre, se posso aggiungere, avere una classe builder Builder con molti degli stessi metodi di Dot non è probabilmente una buona idea. La tua situazione potrebbe essere diversa e questa potrebbe essere la versione semplificata, quindi se questo non si applica a te, ignora questo consiglio. Sebbene sia mia opinione che Builder sia in gran parte inutile qui. Dot può restituire this dopo ogni setter ed eseguire esattamente la stessa funzionalità. Pensaci.

    
risposta data 13.12.2017 - 08:55
fonte

Leggi altre domande sui tag