Newsgroup: it.comp.lang.c++
Titolo: singleton

In Thinking in C++ II edizione ho letto la parte relativa ai Design patterns
: in particolare mi ha colpito la descizione del 'singleton'.
In pratica consiglia di rendere *il* costruttore privato e di sfruttare
l'inizializzazione di un membro statico per richiamarlo per l'unica volta
che interessa.
In un caso analogo, mi ero (senza essere aiutato in questo da nessuna mano
professionale) limitato alla banalissima soluzione di utilizzare solo membri
(dati e funzioni) statici : per evitare che *uno dei* costruttori venga
chiamato piu' di una basta utilizzare un membro statico che conti le istanze
(utile anche per il distruttore).
Siccome non vedo nessun vantaggio nella soluzione dell'Eckel, evidentemente
non devo aver capito qualcosa !
Anzi, mi sembra che nella soluzione proposta ci siano almeno quattro
svantaggi :
- e' possibile usare un solo tipo di costruttori
- la chiamata dell'unico tipo possibile di costruttore avviene all'avvio del
programma e quindi eventualmente molto prima di quanto ce n'e' bisogno
(potrebbe non essercene mai)
- non e' possibile derivare qualcosa da una simile classe
- le funzioni membro non statiche (e non e' concettualmente sensato che non
lo siano) sono (anche se di poco) meno veloci
I punti 1 e 2 sono in effetti correlati : e' chiaro che potro eseguire uno
solo dei costruttori, ma per sceglierlo dovro' attendere che il programma
venga eseguito fino al punto da avere i dati che mi consentano di compiere
tale scelta.
Andrea Murru
amu...@tiscalinet.it
http://www.pegacity.it/informatica/case/473/index.htm

tale scelta.
Se hai questa esigenza, come dicevo, la pattern singleton
puo` agevolmente accomodarla (overload di getInstance,
ecc). D'altra parte, usando l'approccio delle funzioni statiche,
non puoi avere controllo compile-time che queste funzioni
non siano mai chiamate prima dell'unico-e-solo caso di
chiamata al costruttore (puoi solo dare exception a runtime
se succede, cosa decisamente meno interessante).
I vari casi di singleton possono, ad un livello di astrazione,
essere visti come una istanza del problema di rapporto fra
"oggetto" e "stato".  La concettualizzazione "singleton" e`
probabilmente necessaria se consideriamo oggetto e stato
come inscindibili -- ma c'e` anche l'alternativa di scinderli.
Un modo e` quello che preconizzi -- dire che non ci sono
per nulla oggetti in gioco; ma in questo caso non c'e` poi
nessun "punto naturale" dove porre l'inizializzazione dello
stato, che sussiste anche se non vuoi che sussista alcun
oggetto.  Un modo, per certi versi, diametralmente opposto,
e` la pattern "stato condiviso", pubblicata abbastanza di
recente mi pare su C++ report:
    -- la classe ha tanti oggetti quanti ne servono, ma
    -- tutti questi oggetti condividono l'accesso allo
        _stesso_ stato
I costruttori, cosi`, non sono privati, e i clienti della classe
possono a piacer loro costruire oggetti di questa classe
(in stack, con new, eccetera); i metodi sono pero` non
statici, quindi l'accesso allo stato puo` avvenire solo
attraverso un oggetto; il controllo del tipo "inizializza lo
stato, se gia` non e` stato fatto" puo` avvenire dunque
nei soli _costruttori_ -- se a fronte di ogni costruzione
c'e` un certo numero di accessi, questo ammortizza e
riduce assai il costo di quell'"if" famoso.  In certi casi
puo` convenire mantenere il conteggio degli oggetti ora
attivi, per potere sbarazzarsi dello stato (se esso
consuma significative risorse a venir mantenuto) nel
momento in cui non esiste nessun oggetto della classe,
a prezzo naturalmente di poter "ricostruire" lo stato se
e quando viene nuovamente generato un tale oggetto
(ancora una volta, c'e` qui un sostanziale trade-off di
prestazioni).  Lo stato puo` essere realizzato in due
"parti logiche": risorse (tipicamente memoria) sempre
presenti, altre risorse (memoria e altro) "allocate al
volo" alla prima costruzione di oggetti di quella classe
(ed eventualmente rilasciate, v. sopra, ma non e` detto
che convenga).
Un progetto impostato sul fatto che un oggetto sia
singleton, e acceduto solo tramite getInstance, ovvero
che tutti gli oggetti di una classe, benche` costruiti
normalmente, condividano pero` lo stato, presenta
comunque un vantaggio importante rispetto ad uno
basato sull'accesso a funzioni (globali, o statiche di
classe, che siano): se e quando, nell'evoluzione del
progetto, salta fuori che l'unicita`, ovvero la condivisione,
non sono piu` "incondizionali", ma in un qualsiasi modo
"selettive" (unicita`/condivisione solo entro un certo
thread, oppure solo per gli accessi al servizio da parte
di un certo cliente remoto, ecc ecc), il "singleton" (e
ancor meglio l'impostazione a "stato condiviso") si
puo` generalizzare senza particolare onere sui clienti
della classe (la solita ricompilazione se si sono usate
funzioni inline, naturalmente; se si sono evitate, magari
neanche quella... un relink, e neppure quello se si sono
potute usare librerie dinamiche...), e senza aggravio
di prestazioni eccetto che al momento della getInstance
o costruzione; se si sono usate funzioni statiche (o
globali), ognuna di esse dovra` farsi carico di scoprire
quale set-di-stato (in effetti, quale oggetto...) sia quello
da usare nel caso presente.  Questa puo` essere vista
come una generalizzazione del problema, che in
pratica si ha sempre, di determinare se lo stato vada
inizializzato: overhead che nell'approccio a funzioni
(globali o statiche) si paga a ogni chiamata, nel caso
singleton solo alla getInstance, nel caso shared-state
solo ai costruttori; con l'aggravio che l'overhead in
questione puo` essere assai maggiore che non un
semplice if.  A meno che non si voglia scaricare il
problema di tenere traccia della condizione ai clienti
della classe, con parametri supplementari alle varie
funzioni in questione -- scelta architetturale che gia`
dovrebbe dare i brividi...
La letteratura sulle pattern e` molto ampia, e vivace
nella sua crescita, ma io consiglierei comunque di
partire alla fonte, da quel "Design Patterns", di Gamma
et al, che ha "dato la stura" alla popolarita` dell'intero
movimento in questione.  Non sempre le loro trattazioni
di ogni particolare soggetto hanno retto alla prova del
tempo (4 anni sono tanti...), ma si tratta se non altro
della base di partenza di ogni discussione, e comunque
l'intera, complessiva impostazione della faccenda e`
assai ben posta ed istruttiva.
Alex