Fabbrica statica contro fabbrica come singleton

20

In alcuni dei miei codici, ho una fabbrica statica simile a questa:

public class SomeFactory {

    // Static class
    private SomeFactory() {...}

    public static Foo createFoo() {...}

    public static Foo createFooerFoo() {...}
}

Durante una revisione del codice, è stato proposto che questo dovrebbe essere un singleton e iniettato. Quindi dovrebbe assomigliare a questo:

public class SomeFactory {

    public SomeFactory() {}

    public Foo createFoo() {...}

    public Foo createFooerFoo() {...}
}

Alcune cose da evidenziare:

  • Entrambe le fabbriche sono senza stato.
  • L'unica differenza tra i metodi sono i loro ambiti (istanza vs statica). Le implementazioni sono le stesse.
  • Foo è un bean che non ha un'interfaccia.

Gli argomenti che avevo per diventare statici erano:

  • La classe è senza stato, quindi non ha bisogno di essere istanziata
  • Sembra più naturale essere in grado di chiamare un metodo statico che dover istanziare uno stabilimento

Gli argomenti per la fabbrica come singleton erano:

  • È bello iniettare tutto
  • Nonostante l'apolidia della fabbrica, il test è più semplice con l'iniezione (facile da prendere in giro)
  • Dovrebbe essere preso in giro durante il test del consumatore

Ho alcuni problemi seri con l'approccio Singleton dal momento che sembra suggerire che nessun metodo dovrebbe mai essere statico. Sembra anche suggerire che le utilità come StringUtils debbano essere confezionate e iniettate, il che sembra sciocco. Infine, implica che a un certo punto dovrò prendere in giro la fabbrica, il che non sembra giusto. Non riesco a pensare a quando avrei bisogno di prendere in giro la fabbrica.

Che cosa pensa la comunità? Anche se non mi piace l'approccio singleton, non sembra che ci sia un argomento terribilmente strong contro di esso.

    
posta bstempi 20.11.2013 - 20:07
fonte

3 risposte

13

Perché dovresti separare la tua fabbrica dal tipo di oggetto che crea?

public class Foo {

    // Private constructor
    private Foo(...) {...}

    public static Foo of(...) { return new Foo(...); }
}

Questo è ciò che Joshua Bloch descrive come il suo Articolo 1 a pagina 5 del suo libro, "Effective Java". Java è abbastanza prolisso senza aggiungere classi e singleton aggiuntivi e quant'altro. Ci sono alcuni casi in cui una classe factory separata ha senso, ma sono pochi e lontani tra loro.

Per parafrasare l'Articolo 1 di Joshua Bloch, a differenza dei costruttori, i metodi statici di fabbrica possono:

  • Avere nomi che possono descrivere specifici tipi di creazione di oggetti (Bloch usa probablePrime (int, int, Random) come esempio)
  • Restituisce un oggetto esistente (pensa: peso mosca)
  • Restituisce un sottotipo della classe originale o un'interfaccia implementata
  • Riduci la verbosità (di specificare i parametri del tipo - vedi Bloch per esempio)

Svantaggi:

  • Le classi senza un costruttore pubblico o protetto non possono essere sottoclasse (ma puoi usare costruttori protetti e / o Articolo 16: favorire la composizione sull'ereditarietà)
  • I metodi di produzione statici non si differenziano da altri metodi statici (usa un nome standard come of () o valueOf ())

In realtà, dovresti portare il libro di Bloch al tuo revisore di codice e vedere se entrambi siete d'accordo con lo stile di Bloch.

    
risposta data 20.11.2013 - 21:43
fonte
5

Appena fuori dalla mia testa, ecco alcuni dei problemi con la statica in Java:

  • i metodi statici non vengono riprodotti dalle "regole" OOP. Non è possibile forzare una classe a implementare metodi statici specifici (per quanto ne so). Pertanto non è possibile avere più classi che implementano lo stesso set di metodi statici con le stesse firme. Quindi sei bloccato con one di qualunque cosa tu stia facendo. Non puoi nemmeno sottoclassi una classe statica. Quindi nessun polimorfismo, ecc.

  • poiché i metodi non sono oggetti, i metodi statici non possono essere passati e non possono essere utilizzati (senza fare un sacco di codice di attivazione), tranne per codice che è collegati a loro - il che rende il codice cliente altamente accoppiato. (Per essere onesti, questo non è solo colpa della statica).

  • i campi di statistica sono abbastanza simili ai globali

Hai menzionato:

I have some serious issues with the singleton approach since it seems to suggest that no methods should ever be static. It also seems to suggest that utilities such as StringUtils should be wrapped and injected, which seems silly. Lastly, it implies that I'll need to mock the factory at some point, which doesn't seem right. I can't think of when I'd need to mock the factory.

Il che mi rende curioso di chiederti:

  • quali sono, secondo te, i vantaggi dei metodi statici?
  • credi che StringUtils sia correttamente progettato e implementato?
  • perché pensi che non avrai mai bisogno di prendere in giro la fabbrica?
risposta data 20.11.2013 - 20:42
fonte
0

Penso che l'applicazione che stai sviluppando debba essere abbastanza semplice, altrimenti sei relativamente all'inizio del suo ciclo di vita. L'unico altro scenario a cui posso pensare dove non avresti già incontrato problemi con la tua statistica è se sei riuscito a limitare le altre parti del tuo codice che conoscono la tua Factory in una o due posizioni.

Immagina che la tua Applicazione si evolva e ora in alcuni casi devi produrre una FooBar (sottoclasse di Foo). Ora devi esaminare tutti i punti del tuo codice che conoscono il tuo SomeFactory e scrivere una sorta di logica che guarda someCondition e chiama SomeFactory o SomeOtherFactory . In realtà, guardando il tuo codice di esempio, è peggio di quello. Pianifichi solo aggiungendo metodo dopo metodo per creare Foos differenti e tutto il codice cliente deve controllare una condizione prima di capire quale Foo fare. Ciò significa che tutti i clienti devono avere una conoscenza approfondita di come funziona la tua fabbrica e tutti hanno bisogno di cambiare ogni volta che un nuovo Foo è necessario (o rimosso!).

Ora immagina se hai appena creato un IFooFactory che rende IFoo . Ora, produrre una sottoclasse diversa o anche un'implementazione completamente diversa è un gioco da ragazzi - basta inserire il giusto IFooFactory più in alto nella catena e poi quando il client lo riceve, chiama gimmeAFoo() e otterrà il giusto piolo perché ha ottenuto la fabbrica giusta.

Potresti chiederti come si svolge nel codice di produzione. Per darti un esempio della base di codice su cui sto lavorando che inizia a livellarsi dopo poco più di un anno di sviluppo di progetti, ho un sacco di fabbriche che sono usate per creare oggetti dati di domanda (sono qualcosa come Modelli di presentazione ). Ho un QuestionFactoryMap che è fondamentalmente un hash per cercare quale factory di domande usare quando. Quindi, per creare un'applicazione che faccia domande diverse, metto diverse fabbriche sulla mappa.

Le domande possono essere presentate in Istruzioni o Esercizi (che implementano entrambi IContentBlock ), quindi ogni InstructionsFactory o ExerciseFactory otterrà una copia di QuestionFactoryMap. Quindi, le varie fabbriche di Istruzioni ed Esercizi vengono consegnate al ContentBlockBuilder che mantiene di nuovo un hash di cui usare quando. E poi quando l'XML è caricato, il ContentBlockBuilder richiama la Factory Exercise o Instructions dall'hash e lo chiede a createContent() , e poi la Factory delega la costruzione delle domande a qualunque cosa estrae dalla sua QuestionFactoryMap interna in base a ciò che è in ogni nodo XML rispetto all'hash. Purtroppo , quella cosa è un BaseQuestion invece di un IQuestion , ma questo dimostra che anche quando fai attenzione al design, puoi commettere errori che possono perseguitarti.

Il tuo istinto ti sta dicendo che queste statistiche ti faranno del male, e c'è sicuramente abbondante là fuori in letteratura per sostenere il tuo istinto. Non perderai mai avendo l'iniezione di dipendenza che finirai usando solo per i tuoi Test di unità (non che io creda che se costruisci un buon codice che non ti troverai a usarlo più di così), ma la statica può distruggere completamente la tua abilità per modificare rapidamente e facilmente il tuo codice. Allora, perché anche andare lì?

    
risposta data 21.11.2013 - 06:07
fonte

Leggi altre domande sui tag