Su richiesta di exizt, sto espandendo il mio commento a una risposta più lunga.
L'errore più grande che la gente fa è credere che le interfacce siano semplicemente classi astratte vuote. Un'interfaccia è un modo per un programmatore di dire: "Non mi interessa cosa mi dai, purché segua questa convenzione".
La libreria .NET è un ottimo esempio. Ad esempio, quando scrivi una funzione che accetta un IEnumerable<T>
, quello che stai dicendo è "Non mi interessa come stai memorizzando i tuoi dati. Voglio solo sapere che posso usare un foreach
loop.
Questo porta ad un codice molto flessibile. Improvvisamente per essere integrati, tutto ciò che devi fare è giocare secondo le regole delle interfacce esistenti. Se l'implementazione dell'interfaccia è difficile o confusa, forse questo è un suggerimento che stai cercando di spingere un piolo quadrato in un foro rotondo.
Ma poi sorge la domanda: "Che dire del 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'ereditarietà ti consente di scrivere una volta e di utilizzarla ovunque e curerebbe la menangite nel processo di salvataggio degli orfani dai mari nascenti e non ci sarebbero più lacrime e così via ecc. ecc. ecc. "
Usare l'eredità solo perché ti piace il suono delle parole "riutilizzo del codice" è una pessima idea. La guida allo stile del codice di Google rende questo punto abbastanza conciso:
La composizione è spesso più appropriata dell'eredità. ... [B] poiché il codice che implementa una sottoclasse viene distribuito tra la base e la sottoclasse, può essere più difficile comprendere un'implementazione. La sottoclasse non può ignorare le funzioni che non sono virtuali, quindi la sottoclasse non può cambiare l'implementazione.
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 potrebbe sostenere che dovresti provare a ereditare FileStream
, per non duplicare FileStream
il codice. Le persone intelligenti riconoscono che questo è stupido. Dovresti semplicemente avere un FileStream
oggetto nella tua classe (come variabile locale o membro) e usare la sua funzionalità.
L' FileStream
esempio può sembrare inventato, ma non lo è. Se hai due classi che implementano entrambe la stessa interfaccia nello stesso identico modo, allora dovresti avere una terza classe che incapsula qualsiasi operazione duplicata. Il tuo obiettivo dovrebbe essere quello di scrivere classi che siano blocchi riutilizzabili autonomi che possono essere messi insieme come lego.
Ciò non significa che l'eredità debba essere evitata a tutti i costi. Ci sono molti punti da considerare, e la maggior parte verrà affrontata ricercando la domanda "Composizione contro ereditarietà". Il nostro Stack Overflow ha alcune buone risposte sull'argomento.
Alla fine della giornata, i sentimenti del tuo collega mancano della profondità o della comprensione necessarie per prendere una decisione informata. Cerca l'argomento e scoprilo da solo.
† Quando si illustra l'eredità, tutti usano gli animali. Questo è inutile. In 11 anni di sviluppo, non ho mai scritto una classe chiamata Cat
, quindi non la userò come esempio.
alligator
L'implementazione dieat
differisce, ovviamente, in quanto accettacat
edog
come parametri.