Newsgroup: it.comp.lang.c++
Titolo: I costruttori devono sempre costruire un oggetto??

Benchè la risposta alla domanda in oggetto sembra ovvia e positiva, in
realtà non lo è poi tanto. La questione riguarda esclusivamente i
costruttori di oggetti con parametri passati. Ogni parametro, in
generale, ha un range di validità fuori dal quale il metodo non può
operare. Generalmente si risolve questa questione tornando un esito che
indica il fallimento dell'operazione tentata. Ma un costruttore non ha
nessun esito da tornare... Se si lavorasse in C la cosa sarebbe
semplice: basterebbe far tornare al costruttore in puntatore
all'oggetto (this) che però vale NULL in caso di errore. In C++ non so
quale sia il modo migliore per far fronte a questa situazione; mettere
nell'oggetto un menbro del tipo:
bool ok; // esito del successo del costruttore
che il costruttore può mettere a true o a false e che tutti gli altri
metodi testano mi sembra una cosa un po' rozza.
Qualcuno mi aiuta ad allargare il mio orizzonte? :-)) 
Grazie!

 Qualcuno mi aiuta ad allargare il mio orizzonte? :-)) 
Usa try..catch e throw per lanciare le tue eccezioni.
E comunque chi l'ha detto che una funzione con return type void non 
possa restituire valori?
Kiuhnm

Una soluzione č usare una classe builder che chiama il costruttore e poi 
"testa" l'oggetto costruito, distruggendolo se non passa l'esame, e 
restituisce un riferimento o un puntatore all'oggetto stesso.
Ma non sarebbe piů semplice, nel tuo caso, testare i parametri da 
passare PRIMA di chiamare il costruttore?

 realtà non lo è poi tanto.
Lo è invece e la risposta è che i costruttori si chiamano costruttori
*proprio perché* inizializzano lo stato di un'istanza.  Il costruttore
può sollevare un'eccezione, se qualcosa va storto.  In quel caso,
l'oggetto viene immediatamente distrutto (sarebbe quindi bene che il
distruttore gestisse anche casi di costruzione parziale di un oggetto).


Questo e` un problema di design ben noto.
La cosa si risolve con uno dei tanti pattern creazionali (vedi builder, 
abstract factory, factory method ecc. ecc.).
In pratica puoi usare una funzione, detta appunto factory, che ti testa 
i parametri e che chiama la new usando il costruttore parametrico solo 
se i parametri vanno bene altrimenti restituisce errore.
Di solito la funzione restituisce un puntatore all'oggetto costruito 
oppure puntatore nullo se i parametri non vanno bene.
Ciao,
Cesare


Se un costruttore termina prematuramente il distruttore dell'oggetto
*non* viene chiamato. Vengono invece chiamati i distruttori di tutti i
sotto-oggetti (classi base, membri dati) gia' completamente costruiti.
Ciao,
Nicola

gestendo tutto con le eccezioni non mi sembra un problema cosi grosso 
....certo ... in C le eccezioni nn ci sono ma è una classica situazione 
in cui le trovo molto "comode" ....
a.pell...@prod-el.com
 ha scritto:


 sotto-oggetti (classi base, membri dati) gia' completamente costruiti.
Hai ragione! (in realtà ho confuso con Delphi, un altro linguaggio in
cui invece questo avviene).

 nell'oggetto un menbro del tipo:
Come ti e' stato detto esistono svariati modi per porre rimedio a questo 
problema.
Io di solito uso una "static factory" ogniqualvolta mi trovo di fronte 
ad un oggetto la cui costruzione puo' fallire, ma ci sono tecniche piu' 
raffinate. Dipende tutto dalle tue esigenze e dai tuoi gusti.
Qui gli esempi sono in java, ma anche se non sai il linguaggio credo che 
non faticherai molto a capire :)
http://en.wikipedia.org/wiki/Factory_method_pattern

 passare PRIMA di chiamare il costruttore?
L'idea base è che l'oggetto incapsuli le conoscenze che permettono di
validare i parametri in ingresso: chi meglio di lui sa se l'insieme dei
parametri d'ingresso è corretto?
Colgo l'occasione per ringraziare tutti per le risposte! (...e vado a
studiarmi i design pattern!)

 sotto-oggetti (classi base, membri dati) gia' completamente costruiti.
Uhm quindi il problema sollevato dal nostro amico si potrebbe risolvere 
con un costruttore che prima di ogni altra operazione testa i suoi 
parametri e se non vanno bene lancia una apposita eccezione, che 
intercettata permette al sistema di prendere le misure del caso?
Così non sarebbe necessaria la classe factory, però si dovrebbero 
gestire le eccezioni in ogni punto in cui viene creato un nuovo oggetto 
di quel tipo, che suppongo siano sparpagliati qua e là nel codice... 
quale sistema è migliore?

 studiarmi i design pattern!)
Bravo. E scoprirai che la soluzione ottima per il problema "creazione di 
oggetti" e` usare un metodo o oggetto factory che incapsula la suddetta 
creazione di qualunque forma sia: validazione parametri come scelta di 
quale oggetto costruire.
In questo modo anche il codice cliente (chiamante) si semplifica perche` 
delega tutti i test di validazione all'oggetto (o funzione) factory.
Ciao,
Cesare

 passare PRIMA di chiamare il costruttore?
Nel progetto sul quale lavoro abbiamo una marea di oggetti complessi e
a suo tempo ho stabilito la convenzione che ciascun oggetto fosse
costruito da una fabbrica apposita che reperisse nel modo opportuno i
parametri da fornire all'oggetto in costruzione.
Originariamente cio' avveniva costruendo l'oggetto in uno stato
iniziale consistente ma incompleto e successivamente passando via via i
parametri necessari attraverso metodi "set". Col tempo pero' mi sono
accorto che questo approccio rende le classi inutilmente complesse,
perche' ogni volta che e' necessario usare un membro dato particolare
nel corso di questo complesso processo di inizializzatione bisogna
verificare di avere gia' tutti i pezzi che servono e perche'
tipicamente ci si trova a dover compiere le operazioni in un ordine
particolare ed e' facile sbagliare. Tutto questo per l'unico vantaggio
di non passare miriadi di parametri al costruttore.
Cosi' da un po' di tempo sto modificando il mio approccio: la fabbrica
resta, perche' sono convinto che un'automobile e' un'automobile e non
una fabbrica di automobili e perche' e' inutile aggiungere
complicazione a una classe complessa dotandola della logica necessaria
per reperire i propri membri dati, pero' recupero tutti i parametri
necessari *prima* di costruire l'oggetto in questione e poi li passo
tutti al costruttore.
La logica di validazione dei parametri e' strutturata: ogni oggetto
passato deve essere in uno stato consistente e corretto ed il
costruttore dell'oggetto verifica che la combinazione degli stati dei
parametri sia corretta, altrimenti lancia un'eccezione (oppure fa
fallire un'asserzione).
Se poi il mio oggetto originario e' molto complesso lo strutturero' in
una gerarchia di classi, ottenendo un'ulteriore sotto-struttura anche
per il processo di inizializzazione, dove questa volta i vincoli di
ordine tra le operazioni sono garantiti dall'ordine di chiamata dei
costruttori.
Ciao,
Nicola

 quale sistema è migliore?
La classe factory a mio avviso non serve tanto a validare i parametri,
quanto a reperirli, ad esempio leggendoli da un database o da un file.
Se il reperimento e' immediato, la factory non serve.
Un altro caso in cui le factory sono utili e' per evitare
l'acquisizione diretta di singleton, che limita la modularita' del
codice.
Ciao,
Nicola

 Se il reperimento e' immediato, la factory non serve.
Ciao a tutti!
Mi sono andato di corsa a prendere il libro sui Design Pattern e l'ho
aperto sul factory
method(
http://lci.cs.ubbcluj.ro/~raduking/Books/Design%20Patterns/contfs.htm
).
Da quello che ho capito mi pare che il metodo virtuale puro
"CreateDocument" della classe  astratta "Application" debba essere
definito nella classe MyApplication (derivata da "Application"). Nel
corpo del metodo MyApplication::CreateDocument uno potrebbe controllare
i parametri passati al costruttore di MyDocument per verificarne la
validità.
Se è a questo controllo che alludete quando dite che il design pattern
"Factory Method" risolve il problema che ho sollevato non sono
daccordo! Infatti, per eseguire il controllo bisogna che la classe
MyApplication abbia conoscenze tipiche della classe MyDocument e ciò
va contro il principio dell'information hiding (se sto dicendo fesserie
non sparatemi per favore!).
Quello che mi pare faccia il design pattern "Factory Method" è
definire un'interfaccia che consenta ai metodi definiti della classe
base Application di operare su un oggetto di un tipo che non conosce.
E' indubbiamente una cosa interessante ma non mi pare che risolva la
questione in oggetto.
Ho detto bene o c'è qualcosa che non va in questo ragionamento?
Concordo invece con tutti quelli che suggeriscono di usare il gestore
delle eccezioni. Purtroppo nel mio caso la cosa è un po' rognosa
perchè lavoro nel campo dell'embedded con uno RTOS real-time
multi-threading: fino ad oggi ho sperato di non dover utilizzare le
eccezioni ma forse dovrò rivalutare la questione.
Ciao a tutti!
Albi

 
Puoi approfondire questo concetto?
grazie.
BB


Supponi di avere una classe singleton:
class singleton {
  public:
    static singleton * get_instance();
 };
e una classe cliente che ne usa i servizi. Se la classe cliente si
appropria direttamente del singleton, ad es.
class cliente {
  public:
    cliente() : sing(singleton::get_instance()) {}
  private:
    singleton * sing;
};
La connessione tra cliente e singleton e' indissolubile e per fornire
un singleton diverso (ad esempio un'altra classe che fornisce
funzionalita' alternative con la stessa interfaccia) e' necessario
modificare singleton, con ricaduta su *tutti* i suoi clienti.
Se invece cliente e' parametrizzata rispetto a singleton, come in:
class cliente {
  public:
    cliente(singleton * s) : sing(s) {}
  private:
    singleton * sing;
};
Fornire implementazioni alternative di singleton e' molto piu'
semplice. Si pone pero' il problema di reperire l'istanza di singleton
e questo puo' rendere utile la factory.
Ciao,
Nicola

 oppure puntatore nullo se i parametri non vanno bene.
O in certi casi meglio ancora usare il NullObject pattern...
-- 
-enrico

 quale oggetto costruire.
Io  direi  che  il  metodo  ottimo dipende  dall'analisi  del  problema.  IMHO
semplicemente  indicare  una  "ricetta  buona  per  tutti  gli  usi"  porta  a
complicazioni                          non                         necessarie.
-- 
-enrico

 come tradurre decoupling?
Disaccoppiamento.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 complicazioni                          non                         necessarie.
Fare una funzione in piu` e` una complicazione?
Ciao,
Cesare

 eccezioni ma forse dovrò rivalutare la questione.
Anchio lavoro in quel campo ma almeno una funziocella factory la faccio 
sempre.
Ciao,
Cesare

 
Tutto chiaro,
Grazie

 passare PRIMA di chiamare il costruttore?
Se bastasse controllare i parametri sarebbe tutto molto semplice.
Cosa si fa nella realtà?
Si creano i componenti e poi si testano.
Quelli che non soddisfano le specificazioni vengono eliminati.
Kiuhnm

 Quindi non puo` non risolvere il problema.
Il problema posto era: << il costruttore di una classe può non
costruire un oggetto?>> Questo problema non mi sembra risolto dal
design pattern Factory. Sbaglio? Se si, dove?? (abbiate pazienza se son
duro a capire).
Ciao
Albi

 method(
http://lci.cs.ubbcluj.ro/~raduking/Books/Design%20Patterns/contfs.htm
).
Io non ho fatto alcun riferimento a "Design Pattern". E' un ottimo
libro, ma non e' la bibbia.
Ciao,
Nicola

 duro a capire).
Si, ma Io preferisco incapsulare questa scelta in un oggetto esterno, 
una funzione/classe factory.
Ciao,
Cesare

 Cosi' da un po' di tempo sto modificando il mio approccio:
Mi piace il tuo approccio odierno.

 Fare una funzione in piu` e` una complicazione?
Se la funzione nel caso specifico *non* è necessaria, si, è una
complicazione. Se lo è, invece è una cosa necessaria.
Non si sviluppa software a partire da principi primi (una factory è più
versatile, quindi si usa sempre una factory). Si conoscono i principi e
si valuta sulla base della propria esperienza quando la maggiore
versatilità sia necessaria e quando sia non necessaria.
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

Nicola Musatti ha scritto:

Perdonami, nella mia ignoranza credevo fosse la "Bibbia"! Se non lo è
qual'è secondo te la "bibbia"? Se non c'è una "bibbia" riconosciuta
hai qualche valido suggerimento sull'argomento?
Thanks!
Albi

 Cesare
Ciao Cesare
il tuo è un punto di vista interessante. Lo terrò presente.
Una cortesia: hai qulche riferimento a dove viene definnita l'abstract
factory?
Grazie e ciao.
Albi

 hai qualche valido suggerimento sull'argomento?
Sui pattern "Design Patterns" e' certamente il primo libro da leggere,
ma resta il fatto che non e' la "Bibbia", e soprattutto i pattern non
sono comandamenti.
Ciao,
Nicola

Credo che il modo migliore per risolvere il problema di un costruttore
che debba poter costruire un oggetto SENZA usare le eccezioni sia
facilmente mutuabile dalla strategia del singelton che consiste nel
mascherare il costruttore mettendolo privato della classe. Una funzione
della classe (statica e  pubblica) validerà prima i parametri in
ingresso e poi, se tutto OK, provvederà a chiamare il costruttore
tornandone il puntatore all'oggetto creato.
L'unico svantaggio di questa soluzione è che si rende impossibile
costruire un oggetto sullo stack.
Esempio: classe che costruisce un intero con valore maggiore di 10.
class Over10 {
 int intero;
 Over10 (int i) {intero=i;};
 Over10 (void);
public:
 Over10* Over10constructor (int i) {return (i<=10)? NULL; new Over10
(i);};
}

Questo problema è trattato molto brillantemente su "thinking in c++"
volume 2 di Bruce Ekel, al capitolo Exception Handling (Cleaning Up).
Interssante l'utilizzo del class template auto_ptr definito in
<memory>: ha un costruttore che accetta un puntatore ad un tipo
generico ed effettua l'overload degli operatori * e -> che
reindirizzano queste operazioni al puntatore posseduto. In questo modo
l'oggetto auto_ptr può essere utilizzato come il puntatore posseduto.
Quindi se ho
class Cat {
public:
  Cat() { cout << "Cat()" << endl; }
  ~Cat() { cout << "~Cat()" << endl; }
  void g() {}
};
class Dog {
public:
  void* operator new(size_t sz) {
    cout << "allocating a Dog" << endl;
    throw 47;
  }
  void operator delete(void* p) {
    cout << "deallocating a Dog" << endl;
    ::operator delete(p);
  }
};
class UseResources {
  auto_ptr<Cat> cat(new Cat());
  auto_ptr<Dog> dog(new Dog());
public:
  UseResources() { cout << "UseResources()" << endl; }
  ~UseResources() { cout << "~UseResources()" << endl; }
 void f() { cat->g(); }
};
int main() {
  try {
    UseResources ur;
  } catch(int) {
    cout << "inside handler" << endl;
  } catch(...) {
    cout << "inside catch(...)" << endl;
  }
}
ottengo il seguente output:
Cat()
allocating a Dog
~Cat()
inside handler