Mi è sempre piaciuta l'idea di avere l'ereditarietà multipla supportata in una lingua. Molto spesso però è stato intenzionalmente dimenticato, e la presunta "sostituzione" sono interfacce. Le interfacce semplicemente non coprono tutto lo stesso terreno dell'ereditarietà multipla, e questa restrizione può occasionalmente portare a più codice della caldaia.
L'unica ragione di base che io abbia mai sentito per questo è il problema dei diamanti con le classi base. Non posso accettarlo. Per me, viene fuori molto, come "Beh, è possibile rovinare tutto, quindi automaticamente è una cattiva idea". Puoi rovinare qualsiasi cosa in un linguaggio di programmazione, e intendo qualsiasi cosa. Non riesco proprio a prenderlo sul serio, almeno non senza una spiegazione più approfondita.
Essere consapevoli di questo problema è il 90% della battaglia. Inoltre, credo di aver sentito anni fa qualcosa su una soluzione generale che coinvolge un algoritmo di "inviluppo" o qualcosa del genere (questo suona un campanello, qualcuno?).
Per quanto riguarda il problema del diamante, l'unico problema potenzialmente genuino che mi viene in mente è se stai cercando di utilizzare una libreria di terze parti e non riesci a vedere che due classi apparentemente non correlate in quella libreria hanno una classe base comune, ma oltre a documentazione, una semplice funzionalità linguistica potrebbe, diciamo, richiedere che tu dichiari espressamente la tua intenzione di creare un diamante prima che ne compili uno per te. Con una tale caratteristica, qualsiasi creazione di un diamante è intenzionale, sconsiderata o perché non si è consapevoli di questa trappola.
In modo che tutto ciò che viene detto ... C'è qualche vero motivo per cui la maggior parte delle persone odia l'eredità multipla o è solo un mucchio di isteria che causa più danni che benefici? C'è qualcosa che non vedo qui? Grazie.
Esempio
L'automobile estende WheeledVehicle, KIASpectra estende l'automobile e l'elettronica, KIASpectra contiene la radio. Perché KIASpectra non contiene elettronica?
Perché è un elettronico. L'ereditarietà contro la composizione dovrebbe sempre essere una relazione is-a vs a have-a.
Perché è un elettronico. Ci sono cavi, circuiti stampati, interruttori, ecc. Su e giù per quella cosa.
Perché è un elettronico. Se la batteria si esaurisce in inverno, ci sono tanti problemi come se all'improvviso perdessero tutte le ruote.
Perché non usare le interfacce? Prendi il n. 3, ad esempio. Non voglio scriverlo ancora e ancora, e non voglio davvero creare una bizzarra classe di supporto proxy per fare questo:
private void runOrDont()
{
if (this.battery)
{
if (this.battery.working && this.switchedOn)
{
this.run();
return;
}
}
this.dontRun();
}
(Non capiremo se questa implementazione sia buona o cattiva.) Puoi immaginare come possano esserci diverse di queste funzioni associate a Electronic che non sono correlate a nulla in WheeledVehicle e viceversa.
Non ero sicuro di accontentarmi di quell'esempio o meno, poiché lì c'è spazio per l'interpretazione. Potresti anche pensare in termini di Piano che estende Veicolo e FlyingObject e Bird che estende Animal e FlyingObject, o in termini di un esempio molto più puro.
Traits
: funzionano come interfacce con l'implementazione opzionale, ma hanno alcune restrizioni che aiutano a prevenire l'insorgere di problemi come il problema dei diamanti.
KiaSpectra
non è un Electronic
; esso ha Elettronica, e può essere una ElectronicCar
(che estenderebbe Car
...)