Forse hai appena notato che la gente lo diceva negli ultimi mesi, ma è stato conosciuto da buoni programmatori per molto più tempo. Certamente lo sto dicendo, se del caso, per circa un decennio.
Il punto del concetto è che c'è un grande overhead concettuale per l'ereditarietà. Quando si utilizza l'ereditarietà, ogni singola chiamata al metodo ha una spedizione implicita in essa. Se si dispone di alberi di ereditarietà profonda, o di dispacciamento multiplo, o (ancora peggio) entrambi, allora capire dove il particolare metodo invierà in una particolare chiamata può diventare un PITA reale. Rende il ragionamento corretto sul codice più complesso e rende più difficile il debug.
Lasciatemi fare un semplice esempio per illustrare. Supponiamo che in profondità in un albero di ereditarietà, qualcuno abbia chiamato un metodo foo
. Poi arriva qualcun altro e aggiunge foo
nella parte superiore dell'albero, ma facendo qualcosa di diverso. (Questo caso è più comune con l'ereditarietà multipla.) Ora quella persona che lavora alla classe radice ha infranto l'oscura classe figlia 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 cima non penserebbe di testare la classe figlio, ei test per la classe figlio non pensano di testare i nuovi metodi creati nella parte superiore . (Ammettiamo che ci sono modi per scrivere test unitari che cattureranno questo, ma ci sono anche casi in cui non è possibile scrivere facilmente i test in questo modo.)
Per contro, quando si utilizza la composizione, ad ogni chiamata è generalmente più chiaro a cosa si sta inviando la chiamata. (OK, se stai usando l'inversione di controllo, ad esempio con l'iniezione di dipendenza, allora capire dove va la chiamata può anche diventare problematico, ma di solito è più semplice capirlo.) Questo rende più facile ragionare su di esso. Come bonus, la composizione risulta nell'avere metodi separati l'uno dall'altro. L'esempio sopra non dovrebbe accadere lì perché la classe figlio si sposterebbe verso un componente oscuro e non c'è mai una domanda sul fatto che la chiamata a foo
sia stata pensata per il componente oscuro o l'oggetto principale.
Ora hai perfettamente ragione sul fatto che l'ereditarietà e la composizione sono due strumenti molto diversi che servono due diversi tipi di cose. Sicuramente l'ereditarietà porta il sovraccarico concettuale, ma quando è lo strumento giusto per il lavoro, comporta un sovraccarico concettuale inferiore rispetto al tentativo di non usarlo e fare a mano quello che fa per te. Nessuno che sa cosa stanno facendo direbbe che non si dovrebbe mai usare l'ereditarietà. Ma assicurati che sia la cosa giusta da fare.
Sfortunatamente molti sviluppatori apprendono del software orientato agli oggetti, imparano l'ereditarietà e poi escono per usare la loro nuova ascia il più spesso possibile. Il che significa che cercano di usare l'ereditarietà laddove la composizione era lo strumento giusto. Speriamo che imparino meglio nel tempo, ma spesso questo non accade fino a dopo aver rimosso alcuni arti, ecc. Dicendoli in anticipo che è una cattiva idea, tende ad accelerare il processo di apprendimento e ridurre gli infortuni.