2010-06-22 12 views
5

So che questa domanda ha un titolo simile a questo: C++: calling member functions within constructor? ma sto facendo una domanda più generale.Chiamare le funzioni membro da un costruttore

È buona pratica chiamare le funzioni membro all'interno di un costruttore? Rende la lettura del codice più semplice e preferisco il modo di incapsulare di farlo (cioè ogni blocco di codice ha un unico obiettivo).

Un esempio illustrativo, in pitone:

class TestClass: 
    def __init__(self): 
     self.validate() 

    def validate(self): 
     # this validates some data stored in the class 

È questo un modo migliore di farlo che scrivere il codice validate dentro il costruttore? Ci sono degli svantaggi in questo metodo? Ad esempio, è più costoso l'overhead della funzione?

Personalmente lo preferisco per la leggibilità, ma è solo una mia preferenza.

Acclamazioni

risposta

5

Non penso che ci sia qualcosa di intrinsecamente sbagliato nel chiamare le funzioni membro da un costruttore a condizione che non siano funzioni virtuali.

Il problema con la chiamata di funzioni membro virtuali da un costruttore è che una sottoclasse può sovrascrivere la funzione. Questo costringerà il costruttore a chiamare l'implementazione sovrascritta nella sottoclasse, prima che il costruttore della parte sottoclasse dell'oggetto sia stato chiamato.

In Java, una delle privata, statica o finali modificatori di accesso renderà il metodo sicuro per chiamare da un costruttore, impedendo una chiamata virtuale per il metodo della superclasse. Non penso che queste tecniche siano disponibili in Python.

+0

realtà, chiamando metodi virtuali da un costruttore può essere molto utile. Ad esempio, un metodo virtuale per recuperare un tipo di classe specifico da istanziare nel costruttore base in cui la classe specifica è un discendente di una classe base nota o l'implementatore di un'interfaccia conosciuta. In questo modo puoi mantenere l'istanziazione e la distruzione nella classe base, consentendo al contempo la composizione personalizzata per i discendenti. –

+0

Ti consiglia di "chiamare un metodo su un oggetto non inizializzato" come buona pratica per l'uso generico in tutte le lingue? Preferirei utilizzare i pattern di progettazione AbstractFactory e/o Builder per risolvere il tuo problema di esempio. – richj

0

Sono più familiarità con C++ di Python, ma non vedo alcun problema con chiamando funzioni membro da costruttori, soprattutto quando questa pratica è in grado di scomporre un codice simile da più costruttori. Tutto ciò che riduce la ridondanza è buono nei miei libri.

0

Dal punto di vista della leggibilità è decisamente meglio. Una cosa che dovreste chiedervi qui è se il metodo di validazione è autorizzato a eseguire dopo l'oggetto è inizializzato. In caso contrario, è possibile a) utilizzare una specie di variabile inizializzata privata o b) utilizzare Builder pattern per ottenere gli oggetti in uno stato valido prima di utilizzarli.

Assicurarsi che la funzione sia privata. Non vuoi fare confusione con le sottoclassi che lo sovrascrivono (a meno che ciò non sia richiesto dal design, nel qual caso rendilo astratto/virtuale).

0

Il problema principale con questo è che la funzione membro deve lavorare con un oggetto che può essere solo parzialmente inizializzato. E se (anche accidentalmente) passa un riferimento all'oggetto da qualche altra parte, l'altro codice deve fare lo stesso. Questo può diventare piuttosto confuso e soggetto a errori, specialmente quando si inizia a scavalcare una funzione simile in una sottoclasse.

Quindi, in generale, questa pratica deve essere evitata o almeno limitata a funzioni che non possono essere ignorate e non devono mai passare un riferimento all'oggetto che viene costruito su un altro codice.

2

C'è almeno un associato "Beccato" si dovrebbe essere a conoscenza di:

N3797 12.6.2/14

funzioni membri (comprese le funzioni membro virtuali, 10.3) può essere chiamato per un oggetto in costruzione. Analogamente, un oggetto in costruzione può essere l'operando dell'operatore typeid (5.2.8) o di un dynamic_cast (5.2.7). Tuttavia, se queste operazioni vengono eseguite in un ctor-inizializzatore (o in una funzione chiamata direttamente o indirettamente da un ctor-inizializzatore) prima che tutti i mem-inizializzatore s per classi di base hanno completato, il risultato di l'operazione non è definita. [Esempio:

class A { 
public: 
    A(int); 
}; 

class B : public A { 
    int j; 
public: 
    int f(); 
    B() : A(f()), // undefined: calls member function 
        // but base A not yet initialized 
    j(f()) { }  // well-defined: bases are all initialized 
}; 

class C { 
public: 
    C(int); 
}; 

class D : public B, C { 
    int i; 
public: 
    D() : C(f()), // undefined: calls member function 
        // but base C not yet initialized 
    i(f()) { } // well-defined: bases are all initialized 
}; 

- esempio fine]

Problemi correlati