2009-09-08 13 views
16

Proprio l'altro giorno ho visto il codice che utilizza il cosiddetto modello singleton. Significa qualcosa sulla falsariga diSingleton - Perché usare le classi?

class MySingleton{ 
public: 
    void foo() { ... } 
    static MySingleton&get_instance(){ 
     static MySingleton singleton; 
     return singleton 
    } 
private: 
    MySingleton(){ ... } 
    ~MySingleton(){ ... } 
    int bar; 
}; 

Io vedo perché uno vorrebbe farlo:

  • Effettuare l'istanza globalmente accessibile.
  • Assicurarsi che non ci sia mai più di un'istanza di quella classe.

Tuttavia non vedo perché questo modo di fare è superiore a un paio di funzioni libere. Il modo in cui mi piacerebbe implementare è quello di mettere

namespace some_name{ 
    void foo(); 
} 

nell'intestazione e

namespace some_name{ 
    void foo(){ 
     ... 
    } 
} 

nel file di implementazione. Se ho bisogno di inizializzazione e/o la pulizia io o aggiungere un paio di funzioni che devono essere chiamati in modo esplicito o aggiungo

namespace{ 
    class Dummy{ 
     Dummy(){ ... } 
     ~Dummy(){ ... } 
    }dummy; 
} 

nel file di implementazione.

So che questo è da un punto di vista semantico un singleton, tuttavia vedo la prima variante usata molto più spesso in C++ Code rispetto alla seconda. Perché? Considero la seconda versione leggermente superiore, quindi mi chiedo se mi manca qualcosa di ovvio.

  • La seconda versione è più semplice da implementare e meno soggetta a errori. Nella prima variante manca apposta la funzione di costruzione di copia privata per dimostrarlo. Nella seconda variante non c'è modo di fare questo errore.
  • L'implementazione e l'interfaccia sono meglio separate nella seconda versione. Nel primo tutti i membri privati ​​devono essere dichiarati nell'intestazione. Questo ha il vantaggio di poter riscrivere l'implementazione da zero e non è nemmeno necessario ricompilare nulla che usi il singleton. Quando si utilizza la prima variante è molto probabile che sia necessario ricompilare tutto il codice utente anche cambiando solo i minimi dettagli di implementazione.
  • I dettagli di implementazione sono nascosti in entrambi i casi. Nella prima variante usando private e nella seconda variante usando namespace senza nome.

Potete spiegarmi perché tutti usano la prima variante? Non vedo un singolo vantaggio rispetto al buon vecchio modo di fare le cose in C.

+0

non hai bisogno di un costruttore di copia del pattern Singleton. L'intero scopo del modello singleton è assicurarsi che sia possibile creare una sola e unica istanza della classe. – erelender

+4

Quando si parla di single, è necessario aggiungere che single sono un modello * * Design male e dovrebbe essere evitato per ragioni esposte qui: http://stackoverflow.com/questions/1392315/problems-with-singleton-pattern –

+5

fanatici sono così divertente –

risposta

4

fa questo aiuto?

What is so bad about singletons? http://steve.yegge.googlepages.com/singleton-considered-stupid

riformulato: Un Singleton è una società globale glorificato, quindi basta 'implementare' come un globale.

+1

Supponiamo che il Singleton si apre un file di configurazione o di un flusso di registrazione, id vi ricordate di chiamare la funzione di configurazione globale prima del primo utilizzo del log o di configurazione? –

+0

@MGB è ancora una classe con i vettori e roba –

+0

Non proprio. Manca il punto della mia domanda. Mi riferisco al modo di implementare un singleton in C++. Non alla domanda se si dovrebbe usare uno in primo luogo. –

3

La costruzione di static MySingleton singleton; viene richiamata al primo utilizzo. (Quando viene chiamato get_instance().) Se non viene mai chiamato, non chiama mai il costruttore. Il tuo metodo chiamerà il costruttore al momento della costruzione statica. Il metodo precedente consente di chiamare l'ordine e la tempistica dei costruttori. Il metodo ordinerà la costruzione di ogni singolo singleton in base all'ordine di inizializzazione statica del compilatore.

+0

È l'implementazione definita quando viene eseguito il costruttore di variabili statiche locali. Usando la seconda variante hai un comportamento affidabile. Inoltre è possibile aggiungere una bandiera e ottenere il comportamento pigro in modo portatile. –

+2

È l'implementazione definita quando viene eseguito il costruttore di variabili statiche locali. -> Non è vero quando si trovano in una funzione o un se statment ecc ... La memoria allocata possono ottenere in qualsiasi momento prima del main(), ma il costruttore di questi tipi di statica viene chiamata quando il puntatore del programma passa per la prima volta. Quando vengono definiti al di fuori di una funzione di un certo tipo, quando il costruttore viene chiamato è specifico dell'implementazione. –

+0

@ Ben. L'ordine di valutazione delle variabili di funzione staiche è ben definito. È al primo utilizzo. Così si dà l'equivalente del contrassegnati suggerisci ma fatto dal compilatore (e quindi meno possibilità di un errore (non dico compielr è perfetto, ma di solito meglio di un essere umano)) (anche il gcc è thread-safe, e si spera filettatura devono rimanere al sicuro standard nella prossima versione di C++). –

7

Secondo la linea del partito (E. Gamma, R. Helm, R. Johnson e J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA, 1995, p. 128), il singleton offre i seguenti vantaggi sulla soluzione che proponi.

  • È possibile perfezionare le operazioni e la rappresentazione, ad es. attraverso la sottoclasse.
  • È possibile cambiare idea in un secondo momento e disporre di più istanze.
  • È possibile sovrascrivere i metodi del singleton in modo polimorfico.
  • È possibile configurare l'applicazione in fase di esecuzione inizializzando l'istanza Singleton con la classe necessaria.

Detto questo, nella maggior parte dei casi considero la complessità aggiuntiva eccessiva e uso raramente il modello nel codice che scrivo. Ma posso vedere il suo valore quando progetti un'API che gli altri useranno.

+0

"Puoi cambiare idea in un secondo momento e avere più istanze." In pratica questo non è vero perché, ehm, incoraggia il codice accoppiato. –

+1

@Dustin Getz Come incoraggia il codice accoppiato più di ogni altra cosa? – Imagist

+0

Tutte queste funzioni possono essere eseguite mediante funzioni gratuite e utilizzando internamente i puntatori di funzioni per modificare l'implementazione. L'unica differenza è l'interfaccia. cioè ti piace digitare istanza() o meno. –

0

Il problema con il semplice utilizzo di funzioni libere è che quelli di default non ottengono un comportamento persistente condiviso, come la classe singleton può ottenere con variabili e costanti membro.

Si potrebbe procedere a utilizzare variabili globali statiche per fare la stessa cosa, ma per qualcuno che cerca di capirlo, ciò rende la portata di ciò che devono guardare per capire il comportamento di quelle routine quasi illimitate. Con una classe singleton, tutto è ben organizzato in una classe da esaminare per il lettore.

+0

Non vedo perché sia ​​più difficile imparare a utilizzare variabili globali statiche prive di errori, piuttosto che imparare come fare la roba voodoo soggetta ad errori per assicurarsi che esista solo una istanza. I neofiti normalmente usano le variabili globali troppo spesso. Questo dimostra che il concetto di dati globali è piuttosto intuitivo. –

1

Per avere funzioni + dati statici emulare il modello singleton fare affidamento sull'ambito del file C++ e sulla compilazione separata. Questi sono costrutti compilatori piuttosto che costrutti linguistici. Il modello di classe singleton consente l'incapsulamento dei dati indipendentemente dalla posizione rispetto alle unità di compilazione; è incapsulato correttamente anche se è definito in un file con altre classi e funzioni.

Inoltre, non emulerebbe il comportamento del modello singleton, ma solo quello di un oggetto statico, che non è la stessa cosa. La vita di un singleton è indipendente dalla vita del processo che lo contiene. Il singleton correttamente formato viene istanziato al primo utilizzo, mentre i dati statici vengono istanziati e inizializzati prima che main() venga avviato. Questo può essere un problema, se la costruzione dell'oggetto si basa sull'esistenza di qualche altra entità run-time. Anche l'oggetto Singleton non occupa memoria (tranne il suo puntatore di istanza statico) finché non viene istanziato. Potrebbe anche essere distrutto e ricreato in qualsiasi momento, e in effetti molte volte.

Nota se si modifica il singleton per rendere il costruttore protetto anziché privato, è possibile creare una sottoclasse (e quindi applicare facilmente il pattern singleton di riutilizzo), non è possibile farlo con un oggetto statico o un oggetto con tutto il static membri, o funzioni con dati statici con scope di file, o in qualsiasi altro modo si può provare a andare in giro farlo correttamente.

Non c'è niente di sbagliato nel tuo suggerimento di per sé, basta che tu sappia che non è un modello singleton e che manca della sua flessibilità.

0

Personalmente, io uso i singleton quando voglio avere il controllo di quando il costruttore viene eseguito per la prima volta.

Ad esempio; se ho un singleton per il mio sistema di registro, il codice per aprire un file per la scrittura viene inserito nel costrutto singleton, che è chiamato come; Logger.instance(); nel mio processo di avvio. Se usassi un namespace, avrei questo controllo.

Problemi correlati