Qual è l'alternativa a Singleton


114

Abbiamo una classe che contiene le informazioni di configurazione per l'applicazione. Era un singleton. Dopo alcune revisioni architettoniche, ci è stato detto di rimuovere il singleton. Abbiamo riscontrato alcuni vantaggi nel non utilizzare singleton nello unit test perché possiamo testare diverse configurazioni contemporaneamente.

Senza singleton, dobbiamo passare l'istanza ovunque nel nostro codice. Sta diventando così complicato, quindi abbiamo scritto un wrapper singleton. Ora stiamo portando lo stesso codice su PHP e .NET, mi chiedo se esiste un pattern migliore che possiamo usare per l'oggetto di configurazione.

Risposte:


131

Il blog di Google Testing ha una serie di voci su come evitare Singleton (al fine di creare codice testabile). Forse questo può aiutarti:

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

In breve, trasferiamo tutti i nuovi operatori in una fabbrica. Raggruppiamo tutti gli oggetti di durata simile in un'unica fabbrica.


3
*** Utilizzo dell'iniezione di dipendenza per evitare i single
Justin

Questi articoli sono validi quanto gli standard di programmazione C ++ di Google!

2
Beh, non proprio. Il consiglio "Non utilizzare metodi statici" va dritto contro il principio di interfaccia minima di Scott Meyers / Herb Sutters, ad esempio. Ci sono consigli utili, ma mancano del contributo di più menti.
Matthieu M.

@FrankS perché hai cambiato l'ordine dei link? All'inizio era in buona cronologia.
cregox

@Cawas in realtà non ne ho idea, è stato più di quattro anni fa, quindi immagino di aver avuto alcune ragioni allora :-)
FrankS

15

Il modo migliore è utilizzare invece un pattern Factory. Quando costruisci una nuova istanza della tua classe (in fabbrica) puoi inserire i dati 'globali' nell'oggetto appena costruito, sia come riferimento a una singola istanza (che memorizzi nella classe di fabbrica) o copiando il relativo dati nel nuovo oggetto.

Tutti i tuoi oggetti conterranno quindi i dati che vivevano 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 "miglior modo", ma +1 per una buona alternativa.
tylermac

Il problema con questo approccio è che ogni nuovo oggetto contiene (o fa riferimento) ciò che può potenzialmente essere un'enorme quantità di dati. var_dump () su uno qualsiasi di quegli oggetti contenenti gob si traduce molto rapidamente in enormi elenchi disseminati liberamente di avvertimenti di ricorsione . È dannatamente brutto, non può essere molto efficiente e fa sembrare le cose in tilt. Tuttavia, personalmente non ho trovato un modo migliore. Ho piegato il metodo "factory" usando __construct () per fare riferimento a un globale. Sembra che tutto sia piegato all'indietro, tuttavia, per evitare il temuto Singleton ...
FYA

2
@EastGhostCom: potremmo anche usare il singleton e smettere di provare a complicarci le cose :)
gbjbaanb

5

Potrei affermare l'ovvio qui, ma c'è una ragione per cui non puoi usare un framework di iniezione delle dipendenze come 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 tuoi bean (servizi, DAO, qualunque cosa) non devono preoccuparsi di cercarli.

Questo è l'approccio che adotto di solito!


4

Se usi Spring Framework , puoi semplicemente creare un bean normale. Per impostazione predefinita (o se si imposta esplicitamente 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().

Ottieni il vantaggio della singola istanza, senza l'accoppiamento del pattern Singleton.


3
Oh l'ironia - usa (singleton) i fagioli primaverili per sostituire il tuo singleton ...
Zack Macomber

4

L'alternativa è passare ciò di cui hai bisogno invece di chiedere un oggetto per le cose.


4

non accumulare responsabilità per un singolo oggetto di configurazione poiché finirà in un oggetto molto grande, difficile da capire e fragile.

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

Prova a eseguire il refactoring del codice per evitare un oggetto comune, globale e di grandi dimensioni Configuration. Passa solo i parametri obbligatori alle classi client:

class Server {

    int port;

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

}

dovrebbe essere modificato per:

 class Server {

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

un framework di inserimento delle dipendenze aiuterà molto qui, ma non è strettamente richiesto.


Sì, è davvero un buon punto. L'ho già fatto. Il mio grande oggetto di configurazione implementava interfacce come MailServiceConf, ServerConf .. rispetto al framework di Dependency Injection passa la configurazione alle classi in modo che le mie classi non dipendessero dal grande oggetto Configuration.
Caltuntas

1

È possibile ottenere lo stesso comportamento del singleton utilizzando metodi statici. Steve yegge lo spiega molto bene in questo post.


In realtà l'articolo è abbastanza buono e non dice che dovresti usare invece metodi statici. Afferma invece che anche i metodi statici sono solo singleton e alla fine consiglia di utilizzare il pattern del metodo factory: "Concluderò dicendo che se senti ancora la necessità di utilizzare oggetti Singleton, considera invece di utilizzare il pattern Factory Method. ... "
FrankS

0

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


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

1
È in C ++ - il modello è noto come Monostate.

0

Dipende da quali strumenti / framework, ecc. Vengono utilizzati. Con gli strumenti di inserimento delle dipendenze / ioc è spesso possibile ottenere prestazioni / ottimizzazioni singleton facendo in modo che il contenitore di / ioc utilizzi il comportamento singleton per la classe richiesta (come un'interfaccia IConfigSettings) creando solo un'istanza della classe. Questo potrebbe ancora essere sostituito per il test

In alternativa si potrebbe usare una factory per creare la classe e restituire la stessa istanza ogni volta che la si richiede, ma per il test potrebbe restituire una versione stubbed / derisa


0

Esaminare la possibilità di effettuare la configurazione come interfaccia di richiamata. Quindi il tuo codice sensibile alla configurazione apparirà:

MyReuseCode.Configure(IConfiguration)

Il codice di inizializzazione del sistema apparirà:

Library.init(MyIConfigurationImpl)

0

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


0

Forse non è nemmeno molto pulito, ma potresti forse passare le informazioni che desideri modificare al 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 degli unit test).


-1

I singleton non sono malvagi ma il modello di design è difettoso. Ho una classe di cui desidero solo creare una singola istanza durante il runtime, ma desidero creare più istanze isolate durante i test unitari per garantire risultati deterministici.

DI usando Spring, ecc. È un'ottima opzione ma non l'unica opzione.

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.