2011-02-09 12 views
6

Recentemente abbiamo discusso un difetto mentioned here e una delle parti ha detto qualcosa come "sì, è per questo che i libri dicono che eredità dovrebbe essere evitato.Da dove nasce l'idea che "l'eredità deve essere evitata"?

Sto usando eredità per anni e lo trovo molto utile e conveniente in molti casi di design.Inoltre sono sicuro che la persona che discute ha frainteso ciò che quei "libri" dicono:

C'è davvero un'idea che l'ereditarietà sia una specie di male e debba essere evitata? e dove posso saperne di più?

+0

Beh, non lo so, ma io preferisco eredità me stesso. –

+0

@Tuzo: tutto si riduce a "composizione contro ereditarietà"? – sharptooth

+0

Per lo meno penso che sia una grande parte di esso (tutte le risposte lo menzionano). Anche se devo ammettere che intellettualmente amo davvero l'eleganza concettuale dell'eredità. ("Perché non lo sposo ?!" :)) –

risposta

9

Penso che cosa potrebbe avere significava che l'ereditarietà può essere sfruttata eccessivamente, anche nei casi in cui la composizione sarebbe una soluzione migliore. Questo è discusso in diversi libri, ad es.

L'ereditarietà è un potente mezzo per raggiungere il riutilizzo del codice, ma non è sempre il miglior strumento per il lavoro. Utilizzato in modo inappropriato, porta a software fragile. È sicuro utilizzare l'ereditarietà all'interno di un pacchetto, in cui la sottoclasse e le implementazioni della superclasse sono sotto il controllo degli stessi programmatori. È inoltre possibile utilizzare l'ereditarietà durante l'estensione di classi progettate e documentate in modo specifico per l'estensione (elemento 17). Ereditare da classiche classi concrete attraverso i confini dei pacchetti, tuttavia, è pericoloso. Come promemoria, questo libro utilizza la parola "ereditarietà" per l'ereditarietà dell'implementazione media (quando una classe ne estende un'altra). I problemi discussi in questo articolo non si applicano all'ereditarietà dell'interfaccia (quando una classe implementa un'interfaccia o dove un'interfaccia ne estende un'altra).

Diversamente dall'invocazione del metodo, l'ereditarietà viola l'incapsulamento [Snyder86]. In altre parole, una sottoclasse dipende dai dettagli di implementazione della sua superclasse per la sua funzione corretta. L'implementazione della superclasse potrebbe cambiare dalla versione alla release e, in tal caso, la sottoclasse potrebbe interrompersi, anche se il suo codice non è stato toccato con . Di conseguenza, una sottoclasse deve evolversi in tandem con la sua superclasse, a meno che gli autori della superclasse non abbiano progettato e documentato lo specifico allo scopo di essere esteso.

  • Effective C++ 3rd Edition ha diversi elementi relativi a questo:
    • Articolo 32: "is-a" assicurati che i modelli di ereditarietà pubblica
    • articolo 34: le differenze tra ereditarietà di interfaccia e l'eredità di attuazione
    • Articolo 38: Model "ha-a" o "sono attuate in-termini-di" attraverso composizione
+0

Vedere anche "Modelli di testa prima disegno". –

3

Non è da evitare ogni volta, ma ostacola la flessibilità ovunque tu sia bloccato con un insieme definito di operazioni da una superclasse.L'idea di base del design è "lasciare in eredità le parti fisse e iniettare le parti soggette a modifiche".

In Head First Desing Patterns c'è un buon esempio.

Diciamo che si decide di modellare le anatre con una classe base che implementa fly(). Dì che hai un RubberDuck, che non può volare, ovviamente. Sostituisci fly() con un metodo che non fa nulla. Ma un giorno aggiungi DecoyDuck e dimentica di eseguire l'override di fly() e ottieni un pasticcio. Sarebbe stato meglio costruire il DecoyDuck che inietta il comportamento desiderato (composizione sull'ereditarietà).

+0

puoi davvero dire che un'anatra di gomma è un'anatra perché non vola come un'anatra? se puoi, è fly()) davvero adatto per l'inclusione nella classe base dal momento che non è condiviso da tutti che è anatra .... – Jean

+1

esattamente; l'ereditarietà non è idonea a risolvere questo tipo di problema: se dici "sì, l'anatra di gomma è un'anatra" devi scavalcare il fly() in ogni sottoclasse; se dici "no" devi copiarlo ovunque –

+0

Sono d'accordo con te in linea di principio, ma trovo l'esempio meno convincente. Penso che il problema sia che l'astrazione di RubberDuck non è corretta. Se guardassi un'anatra e un'anatra di gomma e provassi a classificarli, la maggior parte delle persone direbbe che un'anatra è un animale (o un uccello) mentre un'anatra di gomma è un * giocattolo * che sembra un'anatra. –

2

L'ereditarietà non deve essere evitata "così com'è".
L'ereditarietà deve essere utilizzata (e utilizzata solo) quando si desidera definire/descrivere una relazione "è una".

Quando un'entità "non è" un'entità, l'entità non deve ereditare da un'altra entità. In questi casi, è necessario utilizzare la composizione.

2

L'ereditarietà aiuta la generalizzazione (che è upcasting in termini OOP), mentre Composition aiuta a definire il comportamento (Inversion of Control - o Injection of Control).

Uno dovrebbe usare quale concessione più beneficio; ma favorire la composizione rende anche il codice più estensibile. Pertanto, il codice diventa "chiuso alla modifica, ma aperto all'estensione" e quindi "HAS-A è migliore di IS-A".

Un po 'come essere un auto da soli o di possedere un'auto (o no?)

Problemi correlati