Su richiesta di exizt, sto espandendo il mio commento a una risposta più lunga.
L'errore più grande che fanno le persone è credere che le interfacce siano semplicemente classi astratte vuote. Un'interfaccia è un modo per un programmatore di dire: "Non mi importa cosa mi dai, purché segua questa convenzione".
La libreria .NET è un ottimo esempio. Ad esempio, quando scrivi una funzione che accetta IEnumerable<T>
, quello che stai dicendo è "Non mi interessa come stai memorizzando i tuoi dati. Voglio solo sapere che posso usare un ciclo foreach
.
Questo porta ad un codice molto flessibile. All'improvviso per essere integrato, tutto ciò che devi fare è giocare secondo le regole delle interfacce esistenti. Se l'implementazione dell'interfaccia è difficile o confusa, allora forse è un suggerimento che stai cercando di infilare un piolo quadrato in un buco rotondo.
Ma poi sorge la domanda: "E riguardo al riutilizzo del codice? I miei professori CS mi hanno detto che l'ereditarietà era la soluzione a tutti i problemi di riutilizzo del codice e che l'eredità ti permette di scrivere una volta e di usarla ovunque e curerebbe la menangite nel processo di salvare gli orfani dal mare in aumento e non ci sarebbero più lacrime, ecc. ecc. ecc. "
Utilizzare l'ereditarietà solo perché ti piace il suono delle parole "riutilizzare il codice" è un'idea piuttosto brutta. La guida alla creazione di stili di codice da parte di Google rende questo punto abbastanza conciso:
Composition is often more appropriate than inheritance. ... [B]ecause
the code implementing a sub-class is spread between the base and the
sub-class, it can be more difficult to understand an implementation.
The sub-class cannot override functions that are not virtual, so the
sub-class cannot change implementation.
Per illustrare perché l'ereditarietà non è sempre la risposta, userò una classe chiamata MySpecialFileWriter †. Una persona che crede ciecamente che l'ereditarietà sia la soluzione a tutti i problemi sosterrebbe che dovresti provare ad ereditare da FileStream
, per evitare di duplicare il codice di FileStream
. Le persone intelligenti riconoscono che questo è stupido. Devi solo avere un oggetto FileStream
nella tua classe (come variabile locale o membro) e usarne la funzionalità.
L'esempio di FileStream
può sembrare forzato, ma non lo è. Se hai due classi che implementano entrambe la stessa interfaccia nello stesso modo, allora dovresti avere una terza classe che incapsula qualsiasi operazione venga duplicata. Il tuo obiettivo dovrebbe essere quello di scrivere classi che sono blocchi riutilizzabili autonomi che possono essere messi insieme come lego.
Questo non significa che l'eredità dovrebbe essere evitata a tutti i costi. Ci sono molti punti da considerare, e la maggior parte sarà trattata dalla ricerca sulla questione "Composizione contro l'ereditarietà". Il nostro overflow dello stack ha alcune buone risposte sull'argomento.
Alla fine della giornata, i sentimenti del tuo collega mancano della profondità o della comprensione necessaria per prendere una decisione informata. Cerca l'argomento e scoprilo da solo.
† Quando si illustra l'ereditarietà, tutti usano gli animali. È inutile In 11 anni di sviluppo, non ho mai scritto una classe chiamata Cat
, quindi non la userò come esempio.