Costruttori vs metodi di fabbrica [chiuso]


181

Quando si modellano le classi, qual è il modo preferito di inizializzare:

  1. Costruttori, o
  2. Metodi di fabbrica

E quali sarebbero le considerazioni per l'utilizzo di uno di essi?

In alcune situazioni, preferisco avere un metodo factory che restituisce null se l'oggetto non può essere costruito. Questo rende il codice pulito. Posso semplicemente verificare se il valore restituito non è nullo prima di intraprendere un'azione alternativa, in contrasto con il lancio di un'eccezione dal costruttore. (Personalmente non mi piacciono le eccezioni)

Dire, ho un costruttore in una classe che si aspetta un valore id. Il costruttore utilizza questo valore per popolare la classe dal database. Nel caso in cui non esista un record con l'id specificato, il costruttore genera un RecordNotFoundException. In questo caso dovrò racchiudere la costruzione di tutte queste classi all'interno di un blocco try..catch.

Al contrario di ciò, posso avere un metodo factory statico su quelle classi che restituirà null se il record non viene trovato.

Quale approccio è meglio in questo caso, costruttore o metodo di fabbrica?

Risposte:


66

Da pagina 108 di Pattern di progettazione: elementi di software riutilizzabile orientato agli oggetti di Gamma, Helm, Johnson e Vlissides.

Utilizzare il modello Metodo di fabbrica quando

  • una classe non può anticipare la classe di oggetti che deve creare
  • una classe vuole che le sue sottoclassi specifichino gli oggetti che crea
  • le classi delegano la responsabilità a una delle numerose sottoclassi di supporto e si desidera localizzare la conoscenza di quale sottoclasse di supporto è il delegato

21
Il metodo statico di fabbrica è diverso dal modello di progettazione GoF - Metodo di metodo di fabbrica. stackoverflow.com/questions/929021/…
Sree Rama,

Non confrontare con il modello del metodo Factory dei modelli di progettazione GoF.
Sree Rama,

137
questo non mi spiega nulla
Sushant,

@Sushant, perché è così?
PaulD

2
Questa risposta non risponde alla domanda, ma trasmette solo informazioni da leggere per comprendere / spiegare il concetto ... Questo è più un commento.
Crt

202

Chiediti cosa sono e perché li abbiamo. Entrambi sono lì per creare l'istanza di un oggetto.

ElementarySchool school = new ElementarySchool();
ElementarySchool school = SchoolFactory.Construct(); // new ElementarySchool() inside

Nessuna differenza finora. Ora immagina di avere vari tipi di scuola e vogliamo passare dall'utilizzo di ElementarySchool a HighSchool (che deriva da una ElementarySchool o implementa la stessa interfaccia ISchool della ElementarySchool). La modifica del codice sarebbe:

HighSchool school = new HighSchool();
HighSchool school = SchoolFactory.Construct(); // new HighSchool() inside

Nel caso di un'interfaccia avremmo:

ISchool school = new HighSchool();
ISchool school = SchoolFactory.Construct(); // new HighSchool() inside

Ora, se disponi di questo codice in più punti, puoi vedere che l'utilizzo del metodo factory può essere piuttosto economico perché una volta modificato il metodo factory hai finito (se usiamo il secondo esempio con le interfacce).

E questa è la principale differenza e vantaggio. Quando si inizia a gestire gerarchie di classi complesse e si desidera creare dinamicamente un'istanza di una classe da tale gerarchia, si ottiene il codice seguente. I metodi di fabbrica potrebbero quindi prendere un parametro che indica al metodo quale istanza concreta da istanziare. Supponiamo che tu abbia una classe MyStudent e che sia necessario creare un'istanza dell'oggetto ISchool corrispondente in modo che lo studente sia membro di quella scuola.

ISchool school = SchoolFactory.ConstructForStudent(myStudent);

Ora hai un posto nella tua app che contiene la logica aziendale che determina quale oggetto ISchool istanziare per diversi oggetti IStudent.

Quindi - per le classi semplici (oggetti valore, ecc.) Il costruttore va bene (non si vuole sovrastimare la propria applicazione) ma per le gerarchie di classi complesse il metodo di fabbrica è un modo preferito.

In questo modo segui il primo principio di progettazione dalla banda di quattro libri "Programma a un'interfaccia, non a un'implementazione".


2
Anche quando pensi che sia una classe semplice, c'è la possibilità che qualcuno debba estendere la tua classe semplice, quindi il metodo factory è ancora migliore. Ad esempio puoi iniziare con ElementarySchool ma in seguito qualcuno (incluso te stesso) può estenderlo con PrivateElementarySchool e PublicElementarySchool.
jack

10
questa dovrebbe essere la risposta accettata
am05mhz,

2
@David, buona risposta, ma puoi espandere un esempio in cui ogni implementazione di interfaccia può richiedere parametri diversi per la costruzione. Ecco un esempio sciocco: IFood sandwich = new Sandwich(Cheese chz, Meat meat);e in che IFood soup = new Soup(Broth broth, Vegetable veg);modo factory eo builder possono aiutarti qui?
Brian,

1
Ho appena letto altre tre spiegazioni sullo scopo dell'utilizzo in fabbrica, e questa è quella che alla fine ha avuto il "clic" per me. Grazie!
Daniel Peirano,

Perché questa non è una risposta accettata?
Tomas,

74

È necessario leggere (se si ha accesso a) 2 effettivi Java 2 Elemento 1: considerare i metodi di fabbrica statici anziché i costruttori .

Vantaggi dei metodi statici di fabbrica:

  1. Hanno dei nomi.
  2. Non sono tenuti a creare un nuovo oggetto ogni volta che vengono invocati.
  3. Possono restituire un oggetto di qualsiasi sottotipo del loro tipo di ritorno.
  4. Riducono la verbosità della creazione di istanze di tipo con parametri.

Svantaggi dei metodi statici di fabbrica:

  1. Quando si forniscono solo metodi di fabbrica statici, le classi senza costruttori pubblici o protetti non possono essere sottoclassate.
  2. Non sono facilmente distinguibili da altri metodi statici

4
Questo mi sembra piuttosto un grave bug in Java, quindi un problema OOD generale. Esistono numerosi linguaggi OO che non hanno nemmeno costruttori, ma la sottoclasse funziona bene.
Jörg W Mittag,

1
@cherouvim perché principalmente il codice viene scritto usando Costruttori se( factory methods are better than Constructors. ( Item-1 ) ) Effective java
Asif Mushtaq,

Punti buoni. È specifico di Java però. È possibile creare un caso per una funzione del linguaggio che rende i metodi di fabbrica distinguibili da altri metodi statici.
OCDev,

30

Per impostazione predefinita, i costruttori dovrebbero essere preferiti, perché sono più semplici da capire e scrivere. Tuttavia, se è necessario separare in modo specifico le peculiarità costruttive di un oggetto dal suo significato semantico come compreso dal codice client, sarebbe meglio usare le fabbriche.

La differenza tra costruttori e fabbriche è analoga, per esempio, a una variabile e a un puntatore a una variabile. C'è un altro livello di riferimento indiretto, che è uno svantaggio; ma c'è anche un altro livello di flessibilità, che è un vantaggio. Quindi, mentre fai una scelta, ti consigliamo di fare questa analisi dei costi rispetto ai benefici.


17
Quindi, (stile TDD) inizieresti con i costruttori come il modo più semplice per portare a termine il lavoro. E poi refactoring alle fabbriche una volta che inizi a ricevere odori di codice (come la logica condizionale ripetuta che determina quale costruttore chiamare)?
AndyM,

1
Punto molto importante Uno studio sugli utenti che ha confrontato fabbriche e costruttori ha trovato risultati molto significativi che indicano che le fabbriche sono dannose per l'usabilità dell'API: "gli utenti richiedono molto più tempo (p = 0,005) per costruire un oggetto con una fabbrica che con un costruttore" [Il modello di fabbrica nella progettazione API : Una valutazione dell'usabilità ].
mdeff,

12

Usa una factory solo quando hai bisogno di un controllo extra con la creazione di oggetti, in un modo che non si può fare con i costruttori.

Le fabbriche hanno la possibilità di memorizzare nella cache, ad esempio.

Un altro modo di utilizzare le fabbriche è in uno scenario in cui non si conosce il tipo che si desidera costruire. Spesso vedi questo tipo di utilizzo negli scenari di fabbrica dei plug-in, in cui ogni plug-in deve derivare da una baseclass o implementare un qualche tipo di interfaccia. La factory crea istanze di classi che derivano dalla baseclass o che implementano l'interfaccia.


11

Una citazione da "Effective Java", 2a ed., Articolo 1: considerare i metodi statici di fabbrica invece dei costruttori, p. 5:

"Si noti che un metodo di fabbrica statico non è lo stesso del modello di Metodo di fabbrica da Modelli di disegno [Gamma95, p. 107]. Il metodo di fabbrica statico descritto in questo articolo non ha equivalenti diretti in Modelli di disegno."


10

Oltre a "java efficace" (come menzionato in un'altra risposta), un altro libro classico suggerisce anche:

Preferisci metodi di fabbrica statici (con nomi che descrivono gli argomenti) a costruttori sovraccarichi.

Per esempio. non scrivere

Complex complex = new Complex(23.0);

ma invece scrivi

Complex complex = Complex.fromRealNumber(23.0);

Il libro arriva fino a suggerire di rendere Complex(float)privato il costruttore, per costringere l'utente a chiamare il metodo factory statico.


2
Leggendo quella parte del libro mi ha portato qui
Purple Haze,

1
@Bayrem: anche a me, lo stavo rileggendo di recente e ho pensato di aggiungerlo alle risposte.
blue_note il

1
In una nota correlata, si potrebbe trovare utili alcune convenzioni di denominazione elaborate dalla java.time quadro per quanto riguarda la denominazione di from…, to…, parse…, with…, e così via. Tieni presente che le classi java.time sono costruite per essere immutabili, ma alcune di queste convenzioni di denominazione potrebbero essere utili da seguire anche con le classi mutabili.
Basil Bourque,

7

Un esempio concreto da un'applicazione CAD / CAM.

Un percorso di taglio sarebbe realizzato usando un costruttore. È una serie di linee e archi che definiscono un percorso da tagliare. Mentre la serie di linee e archi può essere diversa e avere coordinate diverse, può essere facilmente gestita passando una lista in un costruttore.

Una forma sarebbe realizzata usando una fabbrica. Perché mentre esiste una classe di forme, ogni forma verrebbe impostata diversamente a seconda del tipo di forma. Non sappiamo quale forma inizializzeremo fino a quando l'utente non effettuerà una selezione.


5

Dire, ho un costruttore in una classe che si aspetta un valore id. Il costruttore utilizza questo valore per popolare la classe dal database.

Questo processo dovrebbe sicuramente essere al di fuori di un costruttore.

  1. Il costruttore non dovrebbe accedere al database.

  2. Il compito e il motivo di un costruttore è di inizializzare i membri dei dati e di stabilire l'invariante della classe utilizzando i valori passati al costruttore.

  3. Per tutto il resto, un approccio migliore consiste nell'utilizzare un metodo factory statico o, in casi più complessi, una factory factory o classe builder separata .

Alcune linee guida per i costruttori di Microsoft :

Fai un lavoro minimo nel costruttore. I costruttori non dovrebbero fare molto altro che catturare i parametri del costruttore. Il costo di qualsiasi altra elaborazione dovrebbe essere ritardato fino a quando richiesto.

E

Prendi in considerazione l'utilizzo di un metodo statico di fabbrica invece di un costruttore se la semantica dell'operazione desiderata non si associa direttamente alla costruzione di una nuova istanza.


2

A volte devi controllare / calcolare alcuni valori / condizioni durante la creazione di un oggetto. E se riesce a generare un'eccezione, il costrutto è un pessimo modo. Quindi devi fare qualcosa del genere:

var value = new Instance(1, 2).init()
public function init() {
    try {
        doSome()
    }
    catch (e) {
        soAnotherSome()
    }
}

Dove tutti i calcoli aggiuntivi sono in init (). Ma solo tu come sviluppatore conosci davvero questo init (). E, naturalmente, dopo mesi te ne dimentichi. Ma se hai una factory - fai tutto ciò che ti serve in un metodo per nascondere questo init () dalla chiamata diretta - quindi nessun problema. Con questo approccio non ci sono problemi con la caduta sulla creazione e la perdita di memoria.

Qualcuno ti ha parlato della memorizzazione nella cache. Va bene. Ma devi anche ricordare il modello Flyweight che è bello da usare con il modo Factory.

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.