Esistono alternative valide al modello Singleton GOF?

Affrontiamolo. The Singleton Pattern è un argomento altamente controverso con orde di programmatori su entrambi i lati della recinzione. Ci sono quelli che si sentono come il Singleton non è altro che una variabile globale glorificata, e altri che giurano per modello e lo usano incessantemente. Comunque, non voglio che la polemica di Singleton si trovi al centro della mia domanda. Tutti possono avere un tiro alla fune e sfidarlo e vedere chi vince per tutto quello a cui tengo . Quello che sto cercando di dire è che non credo ci sia una sola risposta corretta e non sto cercando intenzionalmente di infiammare i battibecchi partigiani. Sono semplicemente interessato alle alternative singleton quando pongo la domanda:

Sono le loro alternative specifiche al modello Singleton GOF?

Per esempio, molte volte quando ho usato il modello singleton in passato, sono semplicemente interessato a preservare lo stato / i valori di una o più variabili. Lo stato / i valori delle variabili, tuttavia, possono essere conservati tra ogni istanziazione della class utilizzando variabili statiche invece di utilizzare il modello singleton.

Che altra idea hai?

EDIT: Non voglio davvero che questo sia un altro post su “come usare correttamente il singleton”. Di nuovo, sto cercando modi per evitarlo. Per divertimento, ok? Immagino di dover fare una domanda puramente accademica nella tua migliore voce di trailer cinematografico, “In un universo parallelo in cui non esiste un singleton, cosa potremmo fare?”

Alex Miller in ” Patterns I Hate ” cita il seguente:

“Quando un singleton sembra la risposta, trovo che sia spesso più saggio:

  1. Crea un’interfaccia e un’implementazione predefinita del tuo singleton
  2. Costruisci una singola istanza dell’implementazione predefinita nella parte superiore del tuo sistema. Questo potrebbe essere in una configurazione di Spring, o in codice, o definito in una varietà di modi a seconda del sistema.
  3. Passa la singola istanza in ogni componente che ne ha bisogno (iniezione di dipendenza)

Per comprendere il modo corretto di risolvere i problemi di Singletons, è necessario capire cosa c’è di sbagliato in Singletons (e nello stato globale in generale):

I single nascondono le dipendenze.

Perché è così importante?

Perché se si nascondono le dipendenze si tende a perdere traccia della quantità di accoppiamento.

Potresti obiettare

void purchaseLaptop(String creditCardNumber, int price){ CreditCardProcessor.getInstance().debit(creditCardNumber, amount); Cart.getInstance().addLaptop(); } 

è più semplice di

 void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, String creditCardNumber, int price){ creditCardProcessor.debit(creditCardNumber, amount); cart.addLaptop(); } 

ma almeno la seconda API chiarisce esattamente quali sono i collaboratori del metodo.

Quindi il modo per risolvere il problema Singletons non è quello di usare variabili statiche o locatori di servizi, ma di cambiare le classi Singleton in istanze, che vengono istanziate nello scope dove hanno senso e iniettate nei componenti e nei metodi che ne hanno bisogno. Potresti usare un framework IoC per gestirlo, oppure potresti farlo manualmente, ma l’importante è liberarti del tuo stato globale e rendere esplicite le dipendenze e le collaborazioni.

La soluzione migliore che ho trovato è usare il modello factory per build istanze delle tue classi. Usando il modello, puoi assicurare che esiste una sola istanza di una class che è condivisa tra gli oggetti che la usano.

Anche se sarebbe complicato da gestire, ma dopo aver letto questo post sul blog “Dove sono finiti tutti i singleton?” , sembra così naturale. E per inciso, aiuta molto a isolare i tuoi test unitari.

In sintesi, cosa devi fare? Ogni volta che un object dipende da un altro, riceverà un’istanza di esso solo attraverso il suo costruttore (nessuna nuova parola chiave nella class).

 class NeedyClass { private ExSingletonClass exSingleton; public NeedyClass(ExSingletonClass exSingleton){ this.exSingleton = exSingleton; } // Here goes some code that uses the exSingleton object } 

E poi, la fabbrica.

 class FactoryOfNeedy { private ExSingletonClass exSingleton; public FactoryOfNeedy() { this.exSingleton = new ExSingletonClass(); } public NeedyClass buildNeedy() { return new NeedyClass(this.exSingleton); } } 

Dato che istanziate la vostra fabbrica solo una volta, ci sarà una singola istanziazione di exSingleton. Ogni volta che chiami buildNeedy, la nuova istanza di NeedyClass sarà associata a exSingleton.

Spero che aiuti. Si prega di segnalare eventuali errori.

Spring o qualsiasi altro contenitore IoC fa un lavoro abbastanza buono in questo. Poiché le classi sono create e gestite all’esterno dell’app stessa, il contenitore può creare semplici classi singleton e iniettarle dove necessario.

Non dovresti fare di tutto per evitare qualsiasi schema. L’uso di un modello è una decisione di progettazione o un adattamento naturale (semplicemente cade in posizione). Quando si progetta un sistema, è ansible scegliere di utilizzare uno schema o non utilizzare il modello. Tuttavia, non dovresti fare di tutto per evitare tutto ciò che alla fine è una scelta di design.

Non evito il Pattern Singleton. O è appropriato e lo uso o non è appropriato e non lo uso. Credo che sia così semplice.

L’appropriatezza (o la sua mancanza) del Singleton dipende dalla situazione. È una decisione di progettazione che deve essere presa e le conseguenze di tale decisione devono essere comprese (e documentate).

Il pattern singleton esiste perché ci sono situazioni in cui è necessario un singolo object per fornire un insieme di servizi .

Anche se questo è il caso, considero ancora inappropriato l’approccio alla creazione di singleton usando un campo statico / proprietà globale che rappresenta l’istanza. È inappropriato perché crea una dipendenza nel codice tra il campo statico e l’object no, i servizi forniti dall’object.

Quindi, invece del modello classico, singleton, consiglio di utilizzare il modello ‘like’ del servizio con contenitori serviti , dove invece di usare il singleton attraverso un campo statico, si ottiene un riferimento ad esso attraverso un metodo che richiede il tipo di servizio richiesto.

 *pseudocode* currentContainer.GetServiceByObjectType(singletonType) //Under the covers the object might be a singleton, but this is hidden to the consumer. 

invece di un singolo globale

 *pseudocode* singletonType.Instance 

In questo modo quando vuoi cambiare il tipo di un object da singleton a qualcos’altro, avrai tempo facile a farlo. Inoltre come ulteriore vantaggio non è necessario passare attorno a tutte le istanze di oggetti per ogni metodo.

Vedi anche Inversion of Control , l’idea è che esponendo i singleton direttamente al consumatore, si crea una dipendenza tra il consumatore e l’istanza dell’object, non i servizi object forniti dall’object.

La mia opinione è di hide l’uso del modello singleton quando ansible, perché non è sempre ansible evitarlo o desiderarlo.

Monostate (descritto in Agile Software Development di Robert C. Martin) è un’alternativa a Singleton. In questo schema i dati della class sono tutti statici ma i getter / setter non sono statici.

Per esempio:

 public class MonoStateExample { private static int x; public int getX() { return x; } public void setX(int xVal) { x = xVal; } } public class MonoDriver { public static void main(String args[]) { MonoStateExample m1 = new MonoStateExample(); m1.setX(10); MonoStateExample m2 = new MonoStateExample(); if(m1.getX() == m2.getX()) { //singleton behavior } } } 

Monostate ha un comportamento simile a singleton, ma lo fa in un modo in cui il programmatore non è necessariamente consapevole del fatto che viene utilizzato un singleton.

Se si utilizza un Singleton per rappresentare un singolo object dati, è ansible passare un object dati come parametro del metodo.

(anche se, direi che questo è il modo sbagliato di usare un Singleton in primo luogo)

Se il tuo problema è che vuoi mantenere lo stato, vuoi una class MumbleManager. Prima di iniziare a lavorare con un sistema, il tuo cliente crea un MumbleManager, dove Mambo è il nome del sistema. Lo stato è mantenuto attraverso quello. È probabile che il tuo MumbleManager conterrà un sacchetto di proprietà che contiene il tuo stato.

Questo tipo di stile sembra molto simile a C e non molto simile ad oggetti – troverai che gli oggetti che definiscono il tuo sistema avranno tutti un riferimento allo stesso MumbleManager.

Usa un object semplice e un object fabbrica. La fabbrica è responsabile della sorveglianza dell’istanza e dei dettagli degli oggetti semplici solo con le informazioni di configurazione (che contiene ad esempio) e il comportamento.

In realtà se si progetta da zero evitando Singeltons, non si può avere a che fare senza usare Singletons usando variabili statiche. Quando si usano variabili statiche, si crea anche un Singleton più o meno, l’unica differenza è che si stanno creando diverse istanze di oggetti, tuttavia internamente si comportano tutti come se stessero usando un Singleton.

Puoi forse dare un esempio dettagliato in cui usi un Singleton o dove è attualmente utilizzato un Singleton e stai cercando di evitare di usarlo? Questo potrebbe aiutare le persone a trovare una soluzione più elaborata su come gestire la situazione senza un Singleton.

A proposito, personalmente non ho problemi con Singletons e non riesco a capire i problemi che altre persone hanno nei confronti di Singletons. Non vedo niente di male in loro. Cioè, se non li stai abusando. Ogni tecnica utile può essere abusata e, se viene abusata, porterà a risultati negativi. Un’altra tecnica comunemente usata impropriamente è l’ereditarietà. Nessuno ancora direbbe che l’ereditarietà è qualcosa di brutto solo perché alcune persone lo violano orribilmente.

Personalmente, per me, un modo molto più sensato per implementare qualcosa che si comporta come Singleton consiste nell’utilizzare una class completamente statica (membri statici, metodi statici, proprietà statiche). La maggior parte delle volte la implemento in questo modo (non riesco a pensare a differenze di comportamento dal punto di vista dell’utente)

Penso che il posto migliore per sorvegliare il singleton sia a livello di class. A questo punto, dovresti essere in grado di mappare le interazioni tra le classi e vedere se qualcosa assolutamente, richiede sicuramente che solo 1 istanza di questa class sia mai esistita in qualsiasi momento della vita delle applicazioni.

Se questo è il caso, allora hai un singleton. Se stai lanciando dei singleton come comodo durante la codifica, dovresti davvero rivisitare il tuo design e anche smettere di scrivere i singoli singleton 🙂

E sì, “polizia” è la parola che intendevo qui piuttosto che “evitare”. Il singleton non è qualcosa da evitare (nello stesso modo in cui le variabili goto e globali non sono qualcosa da evitare). Invece, dovresti monitorare il suo utilizzo e assicurarti che sia il metodo migliore per ottenere ciò che vuoi in modo efficace.

Io uso singleton principalmente come “contenitore dei metodi”, senza alcun stato. Se ho bisogno di condividere questi metodi con molte classi e voglio evitare l’onere dell’istanziazione e dell’inizializzazione creo un contesto / sessione e inizializzo tutte le classi lì; tutto ciò che fa riferimento alla sessione ha anche accesso al “singleton” così contenuto.

Non avendo programmato un ambiente intensamente orientato agli oggetti (ad es. Java), non sono completamente all’altezza della complessità della discussione. Ma ho implementato un singleton in PHP 4. L’ho fatto come un modo per creare un gestore di database ‘black-box’ che inizializzava automaticamente e non doveva essere passato su e giù per le chiamate di funzione in un framework incompleto e in qualche modo danneggiato.

Avendo letto alcuni link di pattern singleton, non sono completamente sicuro che lo implementerei ancora nello stesso modo. Quello di cui c’era veramente bisogno erano gli oggetti multipli con memoria condivisa (ad es. L’handle del database reale) e questo è praticamente il significato della mia chiamata.

Come la maggior parte dei modelli e degli algoritmi, l’uso di un singleton “solo perché è bello” è The Wrong Thing To Do. Avevo bisogno di una vera e propria “black-box” chiamata che somigliava molto a un singleton. E IMO è il modo di affrontare la domanda: sii consapevole del modello, ma guarda anche al suo ambito più ampio ea quale livello deve essere univoco.

Cosa intendi, quali sono le mie tecniche per evitarlo?

Per “evitarlo”, ciò implica che ci sono molte situazioni in cui mi imbatto in cui il modello singleton è un adattamento naturale, e quindi che devo prendere delle misure per disinnescare queste situazioni.

Ma non ci sono. Non devo evitare il modello singleton. Semplicemente non sorge.