Forse hai notato persone che lo hanno detto negli ultimi mesi, ma è stato conosciuto da buoni programmatori per molto più tempo. Lo dico certamente dove appropriato da circa un decennio.
Il punto del concetto è che esiste un grande sovraccarico concettuale dell'ereditarietà. Quando si utilizza l'ereditarietà, ogni singola chiamata di metodo ha una spedizione implicita in essa. Se hai alberi di eredità profondi, o spedizione multipla, o (anche peggio) entrambi, quindi capire dove verrà inviato il metodo particolare in una chiamata particolare può diventare una PITA reale. Rende più complesso il ragionamento corretto sul codice e rende più difficile il debug.
Vorrei fare un semplice esempio per illustrare. Supponiamo che nel profondo di un albero ereditario, qualcuno abbia chiamato un metodo foo
. Poi arriva qualcun altro e aggiunge foo
in cima all'albero, ma facendo qualcosa di diverso. (Questo caso è più comune con l'ereditarietà multipla.) Ora quella persona che lavora nella classe radice ha rotto l'oscura classe figlio e probabilmente non se ne rende conto. Potresti avere una copertura del 100% con i test unitari e non notare questa rottura perché la persona in alto non penserebbe di testare la classe figlio, e i test per la classe figlio non pensano di testare i nuovi metodi creati in alto . (Certamente ci sono modi per scrivere test unit che li cattureranno, ma ci sono anche casi in cui non puoi scrivere test in quel modo.)
Al contrario, quando si utilizza la composizione, ad ogni chiamata di solito è più chiaro a cosa si sta inviando la chiamata. (OK, se stai usando l'inversione del controllo, ad esempio con l'iniezione di dipendenza, anche capire dove va la chiamata può diventare problematico. Ma di solito è più semplice da capire.) Questo rende più facile il ragionamento. Come bonus, la composizione si traduce in metodi separati l'uno dall'altro. L'esempio sopra non dovrebbe accadere lì perché la classe figlio si sposterebbe verso qualche componente oscuro, e non c'è mai una domanda sul fatto che la chiamata a foo
fosse intesa per il componente oscuro o l'oggetto principale.
Ora hai perfettamente ragione che eredità e composizione sono due strumenti molto diversi che servono due diversi tipi di cose. Sicuramente l'eredità comporta un sovraccarico concettuale, ma quando è lo strumento giusto per il lavoro, comporta un sovraccarico meno concettuale rispetto al cercare di non usarlo e fare a mano ciò che fa per te. Nessuno che sa cosa stanno facendo direbbe che non dovresti mai usare l'eredità. Ma assicurati che sia la cosa giusta da fare.
Sfortunatamente molti sviluppatori conoscono il software orientato agli oggetti, imparano l'eredità e poi escono per usare la loro nuova ascia il più spesso possibile. Ciò significa che cercano di usare l'ereditarietà dove la composizione era lo strumento giusto. Speriamo che impareranno meglio nel tempo, ma spesso ciò non accade fino a dopo che alcuni arti sono stati rimossi, ecc. Dire loro che è una cattiva idea tende ad accelerare il processo di apprendimento e ridurre gli infortuni.