Come molte domande sullo sviluppo del software, la risposta è "dipende".
Alcuni degli articoli hanno altre domande collegate nelle risposte a questo difensore per costruttori "leggeri" che evitano di fare "lavori reali". Tuttavia, ci sono molte circostanze in cui è opportuno inizializzare e iniziare a lavorare nel costruttore.
Di solito, è appropriato quando l'istanziazione è economica, quando l'istanziazione ha essenzialmente un esito concettuale positivo, e dove non c'è nulla di valido o significativo da fare tra la costruzione e un inizio ipotetico.
Per iniziare, caratterizziamo l'idea di un metodo Start (). In molte situazioni sarebbe invece chiamato Init () o Open () o simili. Probabilmente comprenderebbe cose come l'apertura o la creazione di file, l'apertura di porte o connessioni di rete, la connessione a un database, l'accesso all'hardware, l'avvio di nuovi thread, ecc. Quali sono alcune situazioni in cui avrebbe senso creare un oggetto e iniziare a eseguire tali operazioni correttamente via?
Prendi l'esempio di qualcosa che riguarda i file sul file system. Se prendiamo la posizione che dovremmo svolgere un lavoro minimo nella funzione di costruzione, un utilizzo tipico è l'istanziazione della nostra classe filewriter, quindi chiamata in seguito Open () o simile, quindi in seguito iniziare a fornire i dati in qualche modo. Dal momento che vogliamo che il nostro oggetto sia sempre in uno stato valido, dovremmo verificare che possiamo accedere al file al momento dell'istanza. Se non è possibile accedere al file, è necessario generare un'eccezione durante la costruzione. Se tutto è a posto, possiamo in seguito chiamare Open () e andare al lavoro. Ma aspetta, la vita non è mai così semplice. Abbiamo creato una condizione di competizione, poiché è possibile che tra l'istanziazione e Open (), il file sia diventato inaccessibile. Quindi, avremmo bisogno di racchiudere Open () in un altro try / catch per gestire tale scenario. Come è meglio l'istanziazione e l'apertura in un'unica operazione? Direi che in questo scenario il metodo addizionale è peggio che fare il lavoro nel costruttore.
Le procedure di istanziazione ancora più complesse possono essere eseguite con paradigma in un modo facile di debug e di test, e l'iniezione di dipendenza è spesso il mezzo per farlo. Un'interfaccia database fornita come un'istanza DatabaseConnection è un esempio comune che potresti aver visto in natura. Istanziati, gli oggetti validi vengono progressivamente iniettati in oggetti che li utilizzano, garantendo uno stato valido lungo il percorso.
Alcuni commenti e risposte hanno affermato che a volte, vuoi istanziare il tuo oggetto e poi farlo iniziare il suo lavoro, che per me ha poco senso per gli oggetti economici. È abbastanza comune affermare che le tue variabili sono tanto vicine quanto quando le userai, perché la regola dovrebbe essere diversa con gli oggetti? E se li hai separati, quale valore ti stai fornendo tenendoli separati? Se stai aprendo una porta di rete, nel momento in cui sei pronto, hai tutte le informazioni necessarie per farlo, quindi qual è la vera differenza tra una classe che ti fa chiamare un metodo aggiuntivo?
Facendo il lavoro Start () nel costruttore, si è in grado di garantire che ci sia un unico modo per usare una classe, che il modo in cui uno sia il modo giusto e che il compilatore lo imponga per te. Avendo metodi di avvio separati, si ha un accoppiamento temporale che elimina gli errori di programmazione del tempo di compilazione per gli errori di runtime. In un certo senso, iniziando dal costruttore, raccogliamo alcuni dei benefici concettuali di oggetti immutabili, ma per oggetti che fanno cose.
Ora, come ho detto, a volte la risposta è sì, a volte è no. A volte ha senso avere un metodo Start () esposto. A volte ci si interfaccia con un altro sistema in cui RAII e OOP e paradigmi simili non esistono, e non ci sono eccezioni, ecc. E non si ha scelta. A volte l'istanziazione è molto costosa. In questi casi e altri ha senso avere metodi di avvio separati. Solo tu puoi decidere cosa è appropriato.