Domanda

La variabile globale glorificata - diventa una classe globale gloriosa. Alcuni dicono che rompono il design orientato agli oggetti.

Dammi scenari diversi dal buon vecchio logger in cui ha senso usare il singleton.

È stato utile?

Soluzione

Nella mia ricerca della verità ho scoperto che in realtà ci sono pochissimi "accettabili". motivi per usare un Singleton.

Uno dei motivi che tende a ripetersi all'internet è quello di una "registrazione". classe (che hai menzionato). In questo caso, un Singleton può essere utilizzato al posto di una singola istanza di una classe perché una classe di registrazione di solito deve essere utilizzata più e più volte alla nausea da ogni classe in un progetto. Se ogni classe utilizza questa classe di registrazione, l'iniezione di dipendenza diventa ingombrante.

La registrazione è un esempio specifico di " accettabile " Singleton perché non influisce sull'esecuzione del codice. Disabilita la registrazione, l'esecuzione del codice rimane la stessa. Abilitalo, lo stesso. Misko lo inserisce nel modo seguente in Root Cause of Singletons , " Le informazioni qui scorre in un modo: Dalla tua applicazione al logger. Anche se i logger sono a livello globale, poiché nessuna informazione scorre dai logger all'applicazione, i logger sono accettabili. & Quot;

Sono sicuro che ci sono anche altri validi motivi. Alex Miller, in " Patterns I Hate " ;, le discussioni sui localizzatori di servizi e sull'interfaccia utente lato client potrebbero anche essere" accettabili "; scelte.

Leggi di più su Singleton Ti amo, ma mi stai buttando giù.

Altri suggerimenti

Un candidato Singleton deve soddisfare tre requisiti:

  • controlla l'accesso simultaneo a una risorsa condivisa.
  • verrà richiesto l'accesso alla risorsa da più parti disparate del sistema.
  • può esserci un solo oggetto.

Se il Singleton proposto ha solo uno o due di questi requisiti, una riprogettazione è quasi sempre l'opzione corretta.

Ad esempio, è improbabile che uno spooler di stampa venga chiamato da più di una posizione (il menu Stampa), quindi è possibile utilizzare i mutex per risolvere il problema di accesso simultaneo.

Un semplice logger è l'esempio più ovvio di un Singleton possibilmente valido, ma questo può cambiare con schemi di logging più complessi.

Lettura dei file di configurazione che devono essere letti solo al momento dell'avvio e incapsulamento in Singleton.

Si utilizza un singleton quando è necessario gestire una risorsa condivisa. Ad esempio uno spooler per stampante. L'applicazione deve avere un'unica istanza dello spooler per evitare richieste contrastanti per la stessa risorsa.

O una connessione al database o un file manager ecc.

Sono ragionevoli i singlet di sola lettura che memorizzano alcuni stati globali (lingua dell'utente, percorso del file di aiuto, percorso dell'applicazione). Fai attenzione a usare i singleton per controllare la logica aziendale - il single finisce quasi sempre per essere multiplo

Gestione di una connessione (o di un pool di connessioni) a un database.

Lo userei anche per recuperare e archiviare informazioni su file di configurazione esterni.

Uno dei modi in cui usi un singleton è quello di coprire un'istanza in cui deve esserci un unico "broker". controllo dell'accesso a una risorsa. I singleton sono buoni nei logger perché mediano l'accesso, per esempio, a un file, che può essere solo scritto. Per qualcosa come la registrazione, forniscono un modo per sottrarre le scritture a qualcosa come un file di registro: potresti avvolgere un meccanismo di memorizzazione nella cache per il tuo singleton, ecc ...

Pensa anche a una situazione in cui hai un'applicazione con molte finestre / thread / etc, ma che necessita di un unico punto di comunicazione. Una volta ne ho usato uno per controllare i lavori che volevo avviare la mia applicazione. Il singleton era responsabile della serializzazione dei lavori e della visualizzazione del loro stato a qualsiasi altra parte del programma che fosse interessata. In questo tipo di scenario, puoi guardare un singleton come una specie di "server" classe in esecuzione all'interno dell'applicazione ... HTH

Un singleton dovrebbe essere usato quando si gestisce l'accesso a una risorsa che è condivisa dall'intera applicazione, e sarebbe distruttivo avere potenzialmente più istanze della stessa classe. Garantire che l'accesso al thread delle risorse condivise sia sicuro è un ottimo esempio di dove questo tipo di modello può essere vitale.

Quando usi Singletons, dovresti assicurarti di non nascondere accidentalmente dipendenze. Idealmente, i singoli (come la maggior parte delle variabili statiche in un'applicazione) devono essere impostati durante l'esecuzione del codice di inizializzazione per l'applicazione (static void Main () per eseguibili C #, static void main () per eseguibili java) e quindi passati a tutte le altre classi istanziate che lo richiedono. Questo ti aiuta a mantenere la testabilità.

Un esempio pratico di singleton è disponibile in Test :: Builder , la classe che supporta quasi tutti i moderni moduli di test Perl. Test :: Builder singleton memorizza e negozia lo stato e la cronologia del processo di test (risultati dei test storici, conta il numero di test eseguiti) e cose come dove sta andando l'output del test. Questi sono tutti necessari per coordinare più moduli di test, scritti da autori diversi, per lavorare insieme in un unico script di test.

La storia del singleton di Test :: Builder è educativa. Chiamare new () ti dà sempre lo stesso oggetto. Innanzitutto, tutti i dati sono stati memorizzati come variabili di classe senza nulla nell'oggetto stesso. Ha funzionato fino a quando non ho voluto testare Test :: Builder con se stesso. Quindi avevo bisogno di due oggetti Test :: Builder, un setup come un manichino, per catturare e testare il suo comportamento e output, e uno per essere il vero oggetto di test. A quel punto Test :: Builder è stato trasformato in un oggetto reale. L'oggetto singleton era archiviato come dati di classe e new () lo restituiva sempre. create () è stato aggiunto per creare un nuovo oggetto e abilitare i test.

Attualmente, gli utenti vogliono cambiare alcuni comportamenti di Test :: Builder nel proprio modulo, ma lasciarne altri da soli, mentre la cronologia dei test rimane in comune su tutti i moduli di test. Quello che sta succedendo ora è il monolitico oggetto Test :: Builder che viene suddiviso in pezzi più piccoli (storia, output, formato ...) con un'istanza Test :: Builder che li raccoglie insieme. Ora Test :: Builder non deve più essere un singleton. I suoi componenti, come la storia, possono essere. Ciò spinge la necessità inflessibile di un singleton di un livello. Offre maggiore flessibilità all'utente nel mescolare e abbinare pezzi. Gli oggetti singleton più piccoli ora possono semplicemente archiviare i dati, con i loro oggetti contenenti che decidono come usarli. Permette persino a una classe non Test :: Builder di suonare insieme usando la storia Test :: Builder e i singleton di output.

Sembra esserci una spinta tra il coordinamento dei dati e la flessibilità del comportamento che può essere mitigata mettendo il singleton attorno solo ai dati condivisi con il minor numero di comportamenti possibile per garantire l'integrità dei dati.

Quando si carica un oggetto Proprietà di configurazione, dal database o da un file, è utile averlo come singleton; non c'è motivo di continuare a rileggere i dati statici che non cambieranno mentre il server è in esecuzione.

Penso che l'uso singleton possa essere pensato come lo stesso della relazione molti-in-uno nei database. Se hai molte parti diverse del tuo codice che devono funzionare con una singola istanza di un oggetto, è qui che ha senso usare i singoli punti.

Come tutti hanno detto, una risorsa condivisa, in particolare qualcosa che non può gestire l'accesso simultaneo.

Un esempio specifico che ho visto è un Lucene Search Index Writer.

Risorse condivise. Soprattutto in PHP, una classe di database, una classe modello e una classe di deposito variabile globale. Tutti devono essere condivisi da tutti i moduli / classi che vengono utilizzati in tutto il codice.

È un vero utilizzo dell'oggetto - > la classe modello contiene il modello di pagina che viene creato e viene modellato, aggiunto, modificato dai moduli che si stanno aggiungendo all'output della pagina. Deve essere conservato come una singola istanza in modo che ciò possa accadere, e lo stesso vale per i database. Con un singleton di database condiviso, tutte le classi dei moduli possono accedere alle query e ottenerle senza doverle rieseguire.

Un singleton di deposito di variabili globali ti fornisce un deposito di variabili globale, affidabile e facilmente utilizzabile. Riordina molto il tuo codice. Immagina di avere tutti i valori di configurazione in un array in un singleton come:

$ gb- > config ['hostname']

o con tutti i valori di lingua in un array come:

$ GB- > lang [ 'ENTER_USER']

Alla fine dell'esecuzione del codice per la pagina, ottieni, diciamo, un ormai maturo:

$ template

Singleton, un singleton $ gb che ha l'array lang da sostituire in esso e tutto l'output è caricato e pronto. Li sostituisci semplicemente nelle chiavi che sono ora presenti nel valore di pagina dell'oggetto modello maturo e poi lo distribuisci all'utente.

Il grande vantaggio di questo è che puoi fare QUALSIASI post-elaborazione che ti piace su qualsiasi cosa. Puoi reindirizzare tutti i valori della lingua a google translate o a un altro servizio di traduzione e ripristinarli e sostituirli nei loro luoghi, tradotti, ad esempio. oppure puoi sostituire le strutture di pagina o le stringhe di contenuto come desideri.

È possibile utilizzare Singleton quando si implementa il modello di stato (nel modo mostrato nel libro GoF). Questo perché le classi statali concrete non hanno uno stato proprio e svolgono le loro azioni in termini di una classe di contesto.

Puoi anche rendere Abstract Factory un singleton.

Può essere molto pragmatico configurare problemi specifici dell'infrastruttura come singoli o variabili globali. Il mio esempio preferito di ciò sono i framework di Iniezione delle dipendenze che fanno uso di singoli per fungere da punto di connessione al framework.

In questo caso stai assumendo una dipendenza dall'infrastruttura per semplificare l'utilizzo della libreria ed evitare complessità non necessarie.

Lo uso per un oggetto che incapsula i parametri della riga di comando quando si tratta di moduli collegabili. Il programma principale non sa quali sono i parametri della riga di comando per i moduli che vengono caricati (e non sa nemmeno nemmeno quali moduli vengono caricati). ad es., i carichi principali A, che non richiedono alcun parametro stesso (quindi perché dovrebbe richiedere un puntatore / riferimento extra / qualunque cosa, non sono sicuro - sembra inquinamento), quindi carica i moduli X, Y e Z. Due di questi, diciamo X e Z, hanno bisogno (o accettano) dei parametri, quindi richiamano al singleton della riga di comando per dirgli quali parametri accettare, e in fase di esecuzione richiamano per scoprire se l'utente ha effettivamente specificato di loro.

In molti modi, un singleton per la gestione dei parametri CGI funzionerebbe allo stesso modo se stai usando un solo processo per query (altri metodi mod_ * non lo fanno, quindi sarebbe male lì - quindi l'argomento che dice non dovresti usare i singoli nel mondo mod_cgi nel caso in cui porti su mod_perl o in qualunque altro mondo).

Un esempio con codice, forse.

Qui, il ConcreteRegistry è un singleton in un gioco di poker che consente ai comportamenti fino all'albero del pacchetto di accedere alle poche interfacce di base del gioco (ad esempio, le facciate per il modello, la vista, il controller, l'ambiente, ecc. .):

http://www.edmundkirwan.com/servlet/fractal /cs1/frac-cs40.html

Ed.

1 - Un commento sulla prima risposta:

Non sono d'accordo con una classe Logger statica. questo può essere pratico per un'implementazione, ma non può essere sostituibile per test unitari. Una classe statica non può essere sostituita da un doppio di prova. Se non esegui il test unitario, non vedrai il problema qui.

2 - Cerco di non creare un singleton a mano. Creo semplicemente un oggetto semplice con costruttori che mi consentono di iniettare collaboratori nell'oggetto. Se avessi bisogno di un singleton, utilizzerei un framework di inection di dipendenza (Spring.NET, Unity per .NET, Spring per Java) o qualche altro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top