J'ai toujours aimé l'idée de prendre en charge l'héritage multiple dans une langue. Le plus souvent, c'est intentionnellement abandonné et le supposé "remplaçant" est une interface. Les interfaces ne couvrent tout simplement pas la même terre sur plusieurs héritages multiples, et cette restriction peut parfois conduire à plus de code passe-partout.
La seule raison fondamentale que j'ai jamais entendue pour cela est le problème des diamants avec les classes de base. Je ne peux tout simplement pas accepter ça. Pour moi, ça vient énormément du genre: "Eh bien, il est possible de tout foirer, alors c'est automatiquement une mauvaise idée." Vous pouvez tout gâcher dans un langage de programmation, et je veux dire n'importe quoi. Je ne peux tout simplement pas prendre cela au sérieux, du moins sans une explication plus approfondie.
Le seul fait d'être conscient de ce problème représente 90% de la bataille. De plus, je pense avoir entendu parler il y a quelques années d'une solution polyvalente impliquant un algorithme "enveloppe" ou quelque chose du genre (est-ce que cela vous dit quelque chose, que ce soit?).
En ce qui concerne le problème des diamants, le seul problème potentiellement sérieux auquel je puisse penser est si vous essayez d'utiliser une bibliothèque tierce et que vous ne pouvez pas voir que deux classes apparemment non liées de cette bibliothèque ont une classe de base commune, mais en plus de La documentation, une fonctionnalité de langage simple pourrait, par exemple, exiger que vous déclariez spécifiquement votre intention de créer un diamant avant qu'il n'en compile réellement un pour vous. Avec une telle caractéristique, toute création d’un diamant est soit intentionnelle, soit imprudente, soit parce que nous ne sommes pas conscients de ce piège.
Pour que tout soit dit… Y a-t-il une raison réelle pour laquelle la plupart des gens détestent l'héritage multiple ou est-ce simplement une hystérie qui fait plus de mal que de bien? Y a-t-il quelque chose que je ne vois pas ici? Je vous remercie.
Exemple
Car étend WheeledVehicle, KIASpectra étend Car et Electronic, KIASpectra contient la radio. Pourquoi KIASpectra ne contient-il pas Electronic?
Parce qu'il est un électronique. L'héritage par rapport à la composition doit toujours être une relation is-a versus une relation has-a.
Parce qu'il est un électronique. Il y a des fils, des cartes de circuits imprimés, des commutateurs, etc.
Parce qu'il est un électronique. Si votre batterie s'épuise en hiver, vous aurez autant de problèmes que si toutes vos roues venaient à disparaître.
Pourquoi ne pas utiliser les interfaces? Prenez le numéro 3, par exemple. Je ne veux pas écrire ceci encore et encore, et je ne veux vraiment pas créer une classe d'assistance proxy bizarre pour le faire non plus:
private void runOrDont()
{
if (this.battery)
{
if (this.battery.working && this.switchedOn)
{
this.run();
return;
}
}
this.dontRun();
}
(Nous n'essayons pas de savoir si cette implémentation est bonne ou mauvaise.) Vous pouvez imaginer comment plusieurs de ces fonctions associées à Electronic peuvent ne pas être liées à WheeledVehicle, et inversement.
Je ne savais pas trop si je devais me calmer sur cet exemple, car il y avait place pour l'interprétation. Vous pouvez également penser en termes d’avion et de FlyingObject et d’Animal et de FlyingObject, ou en tant qu’exemple beaucoup plus pur.
Traits
- elles agissent comme des interfaces avec une implémentation optionnelle, mais ont certaines restrictions qui aident à prévenir des problèmes tels que le problème des diamants.
KiaSpectra
n'est pas un Electronic
; il a l' électronique, et peut être un ElectronicCar
(qui s'étendrait Car
...)