OOP Stile di codifica: inizializza tutto sul costruttore?

14

Mi considero ancora un programmatore apprendista, quindi cerco sempre di imparare un modo "migliore" per la programmazione tipica. Oggi il mio collega ha sostenuto che il mio stile di codifica comporta un lavoro non necessario, e voglio sentire le opinioni degli altri. Tipicamente, quando progetto una classe in linguaggio OOP (solitamente C ++ o Python), separerei l'inizializzazione in due parti differenti:

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(o, python equivalent)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

Qual è la tua opinione su questo approccio? Dovrei astenermi dal dividere il processo di inizializzazione? La domanda non è solo limitata a C ++ e Python, e anche le risposte per altre lingue sono apprezzate.

    
posta Caladbolgll 23.11.2016 - 22:24
fonte

5 risposte

28

Anche se a volte è problematico, ci sono molti vantaggi nell'inizializzare tutto nel costruttore:

  1. Se si verifica un errore, ciò accade il più rapidamente possibile ed è più facile da diagnosticare. Ad esempio, se null è un valore di argomento non valido, prova e fallisce nel costruttore.
  2. L'oggetto è sempre in uno stato valido. Un collaboratore non può sbagliare e dimentica di chiamare initMyClass1() perché non è lì . "I componenti più economici, più veloci e più affidabili sono quelli che non ci sono."
  3. Se ha senso, l'oggetto può essere reso immutable che ha molti vantaggi.
risposta data 23.11.2016 - 23:17
fonte
2

Pensa all'astrazione che stai fornendo ai tuoi utenti.

Perché dividere qualcosa che potrebbe essere fatto in un colpo su due?

L'inizializzazione aggiuntiva è solo qualcosa in più per i programmatori che usano la tua API per ricordare e fornisce altro per andare male se non lo fanno correttamente, ma per quale valore per loro per questo onere extra?

Vuoi fornire astrazioni semplici, facili da usare, difficili da sbagliare. La programmazione è abbastanza difficile senza cose gratuite da ricordare / cerchi da saltare. Vuoi che i tuoi utenti API (anche se utilizzi solo la tua API) rientrino nel pit del successo .

    
risposta data 24.11.2016 - 01:07
fonte
1

Inizializza tutto tranne l'area Big Data. Gli strumenti di analisi statica contrassegneranno i campi non inizializzati nel costruttore. Tuttavia, il modo più produttivo / sicuro consiste nell'avere tutte le variabili membro con i costruttori predefiniti e inizializzare esplicitamente solo quelli che richiedono l'inizializzazione non predefinita.

    
risposta data 23.11.2016 - 22:31
fonte
0

Ci sono casi in cui l'oggetto ha molte inizializzazioni che possono essere suddivise in due categorie:

  1. Attributi che sono immutabili o che non devono essere ripristinati.

  2. Attributi che potrebbero aver bisogno di ripristinare i valori originali (o valori basati su modelli) in base ad alcune condizioni dopo aver completato il loro lavoro, tipo una reimpostazione graduale. per esempio. connessioni in un pool di connessione.

Qui la seconda parte dell'inizializzazione viene mantenuta in una funzione separata, ad esempio InitialiseObject (), può essere chiamata nel ctor.

La stessa funzione può essere richiamata in un secondo momento se è necessario un reset software, senza dover scartare e ricreare l'oggetto.

    
risposta data 25.11.2016 - 11:41
fonte
0

Come altri hanno già detto, è generalmente una buona idea inizializzarsi nel costruttore.

Tuttavia, ci sono dei motivi per cui non si può o non si può applicare in casi specifici.

Gestione degli errori

In molte lingue, l'unico modo per segnalare un errore in un costruttore è di sollevare un'eccezione.

Se la tua inizializzazione ha una ragionevole possibilità di generare un errore, ad es. coinvolge IO o i suoi parametri possono essere input dell'utente, quindi l'unico meccanismo a tua disposizione è quello di generare un'eccezione. In alcuni casi, questo potrebbe non essere ciò che si desidera e potrebbe essere più sensato separare il codice soggetto a errore in una funzione di inizializzazione separata.

Probabilmente l'esempio più comune di questo è in C ++ se lo standard di progetto / organizzazione è di disattivare le eccezioni.

Macchina di stato

Questo è il caso in cui si sta modellando un oggetto che ha transizioni di stato esplicite. Ad esempio, un file o un socket che può essere aperto e chiuso.

In questo caso, è normale che la costruzione dell'oggetto (e la cancellazione) riguardi solo gli attributi orientati alla memoria (nome del file, porta ecc.). Ci saranno poi le funzioni per gestire in modo specifico le transizioni di stato, ad es. aprire, chiudere che sono effettivamente funzioni di inizializzazione e demolizione.

I vantaggi sono nella gestione degli errori, come sopra, ma potrebbe anche esserci un caso per separare la costruzione dall'inizializzazione (diciamo che si costruisce un vettore di file e li si apre in modo asincrono).

Lo svantaggio, come altri hanno detto, è che ora metti l'onere della gestione dello stato sull'utente delle tue classi. Se si riuscisse a gestire solo con la costruzione, si potrebbe, ad esempio, utilizzare RAII per farlo automaticamente.

    
risposta data 25.11.2016 - 17:58
fonte

Leggi altre domande sui tag