2009-08-19 10 views
108

Abbiamo una classe che contiene le informazioni di configurazione per l'applicazione. Era un singleton. Dopo una revisione architettonica, ci è stato detto di rimuovere il singleton. Abbiamo visto alcuni vantaggi nel non utilizzare singleton nei test delle unità perché possiamo testare diverse configurazioni tutte in una volta.Qual è l'alternativa a Singleton

Senza singleton, dobbiamo passare l'istanza ovunque nel nostro codice. È diventato così complicato che abbiamo scritto un involucro singleton. Ora stiamo trasferendo lo stesso codice a PHP e .NET, mi chiedo se ci sia un modello migliore che possiamo usare per l'oggetto di configurazione.

risposta

124

Lo Google Testing blog ha una serie di voci su come evitare Singleton (per creare codice verificabile). Forse questo può aiutare:

L'ultimo articolo spiega in dettaglio come spostare la creazione di nuovi oggetti in una fabbrica, in modo da puoi evitare di usare singleton.Vale la pena leggere di sicuro.

In breve spostiamo tutti i nuovi operatori in fabbrica. Raggruppiamo tutti gli oggetti di durata simile in una singola fabbrica.

+11

È necessario leggere il blog di test di Google. – koen

+3

*** Uso dell'iniezione di dipendenza per evitare singleton – Justin

+0

Questi articoli sono buoni come gli standard di programmazione di Google C++! –

0

È una classe che contiene solo metodi e campi statici possibili? Non sono sicuro di quale sia esattamente la tua situazione, ma potrebbe valere la pena esaminarla.

+1

Se la classe è senza stato, dovrebbe essere una classe statica. – AlbertoPL

+1

È in C++ - il pattern è noto come Monostate. –

15

Il modo migliore è utilizzare invece un modello di fabbrica. Quando si costruisce una nuova istanza della classe (in fabbrica) è possibile inserire i dati "globali" nell'oggetto di nuova costruzione, come riferimento a una singola istanza (che si memorizza nella classe factory) o copiando la pertinente dati nel nuovo oggetto.

Tutti i tuoi oggetti conterranno quindi i dati che erano abituati a vivere nel singleton. Non penso che ci sia molta differenza nel complesso, ma può rendere il tuo codice più facile da leggere.

+1

Non sono d'accordo con l'affermazione "modo migliore", ma +1 per una buona alternativa. – tylermac

+0

Il problema con questo approccio è che ogni nuovo oggetto contiene (o riferimenti) ciò che potenzialmente può essere una grande quantità di dati. var_dump() su uno di quegli oggetti contenenti gob risulta molto rapidamente in elenchi enormi disseminati liberamente con ** ricorsioni ** avvertimenti. È terribilmente brutto, non può essere terribilmente efficiente e fa sembrare le cose tese. Tuttavia, non ho trovato personalmente un modo migliore. Ho piegato il metodo "factory" nell'uso di __construct() per fare riferimento a un globale. Sembra che tutto sia piegato all'indietro, comunque, per evitare il temuto Singleton ... – FYA

+2

@EastGhostCom: potremmo anche usare il singleton e smettere di cercare di rendere le cose difficili per noi stessi :) – gbjbaanb

5

Qui potrei affermare l'ovvio, ma esiste un motivo per cui non è possibile utilizzare un'infrastruttura per le dipendenze, ad esempio Spring o Guice? (Credo che Spring sia disponibile anche per .NET ora).

In questo modo, il framework può contenere una singola copia degli oggetti di configurazione ei bean (servizi, DAO, qualsiasi cosa) non devono preoccuparsi di cercarlo.

Questo è l'approccio che prendo di solito!

0

Forse non molto pulita, ma si potrebbe forse passare i bit di informazione che si desidera hanno cambiato il metodo che crea il Singleton - invece di usare

public static Singleton getInstance() { 
    if(singleton != null) 
     createSingleton(); 
     return singleton; 
    } 
} 

si potrebbe chiamare createSingleton(Information info) direttamente all'avvio dell'applicazione (e nei metodi setUp dei test unitari).

4

Se si utilizza Spring Framework, è possibile semplicemente creare un bean regolare. Per impostazione predefinita (o se si imposta in modo esplicito scope="singleton") viene creata solo un'istanza del bean e tale istanza viene restituita ogni volta che il bean viene utilizzato in una dipendenza o recuperato tramite getBean().

Si ottiene il vantaggio della singola istanza, senza l'accoppiamento del modello Singleton.

+1

Oh l'ironia - usa (singleton) Spring fagioli per sostituire il tuo singleton ... –

0

Dipende da quali strumenti/strutture ecc. Vengono utilizzati. Con gli strumenti di input/ioc di dipendenza si può spesso ottenere prestazioni/ottimizzazioni singleton facendo in modo che il contenitore di/ioc usi il comportamento di singleton per la classe richiesta (come un'interfaccia IConfigSettings) creando sempre un'istanza della classe. Questo potrebbe essere ancora sostituito per testare

In alternativa si potrebbe usare una fabbrica per creare la classe e restituire la stessa istanza ogni volta che si richiede - ma per il test potrebbe restituire un/versione deriso contuso

4

L'alternativa sta passando in quello di cui hai bisogno invece di chiedere un oggetto per le cose.

0

Verifica la possibilità di configurare la configurazione come interfaccia di richiamo. codice in modo sensibile la configurazione sarà:

MyReuseCode.Configure(IConfiguration) 

codice di sistema-init sarà:

Library.init(MyIConfigurationImpl) 
4

non si accumulano responsibilites ad un singolo oggetto di configurazione dal momento che sarà termina con una molto grande oggetto che è al tempo stesso difficile da capire e fragile.

Ad esempio se è necessario un altro parametro per una particolare classe, modificare l'oggetto Configuration, quindi ricompilare tutte le classi che lo utilizzano. Questo è un po 'problematico.

Provare a refactoring il codice per evitare un oggetto comune, globale e grande Configuration. Passare parametri solo necessari per le classi client:

class Server { 

    int port; 

    Server(Configuration config) { 
     this.port = config.getServerPort(); 
    } 

} 

dovrebbe essere riscritta a:

class Server { 

    public Server(int port) { 
     this.port = port; 
    } 
} 

un'iniezione quadro dipendenza aiuterà molto qui, ma non è stricly necessaria.

+0

Sì, è davvero un buon punto. L'ho già fatto prima. Il mio grande oggetto di configurazione stava implementando interfacce come MailServiceConf, ServerConf .. che il framework Dipendenze Injection passa la configurazione alle classi, quindi le mie classi non dipendevano dal grande oggetto Configuration. – mcaaltuntas

0

È possibile utilizzare un framework di iniezione delle dipendenze per alleviare il dolore del passaggio nell'oggetto di configurazione. Uno decente è ninject che ha il vantaggio di utilizzare il codice anziché xml.

0

È possibile eseguire lo stesso comportamento di singleton utilizzando metodi statici. Steve yegge lo spiega molto bene nel post this.

+0

In realtà l'articolo è abbastanza buono, e non dice che dovresti usare metodi statici. Invece afferma che i metodi statici sono solo singleton e alla fine raccomanda di utilizzare il modello di metodo di fabbrica: "Chiudo dicendo che se si sente ancora la necessità di utilizzare oggetti Singleton, si consideri l'utilizzo del modello Metodo di fabbrica invece ... " – FrankS

-1

I singleton non sono malvagi ma il modello di progettazione è imperfetto. Ho una classe che voglio solo creare una singola istanza di esso durante il runtime ma voglio creare più istanze isolate durante i test unitari per garantire risultati deterministici.

DI utilizzando Spring, ecc. È un'opzione molto buona ma non l'unica opzione.

+1

Quali sono le altre opzioni? – hakre

Problemi correlati