Pour tenter de comprendre pleinement comment résoudre les multiples problèmes d'héritage de Java, j'ai une question classique que j'ai besoin de clarifier.
Disons que j'ai une classe, Animal
cela a des sous-classes Bird
et Horse
que j'ai besoin de faire une classe Pegasus
qui s'étend de Bird
et Horse
depuis Pegasus
est à la fois un oiseau et un cheval.
Je pense que c'est le problème classique du diamant. D'après ce que je peux comprendre la façon classique c'est de résoudre pour rendre le Animal
, Bird
et les Horse
interfaces des classes et la mise en œuvre Pegasus
de leur part .
Je me demandais s'il y avait une autre façon de résoudre le problème dans lequel je peux encore créer des objets pour les oiseaux et les chevaux. S'il y avait un moyen de créer des animaux aussi, ce serait formidable mais pas nécessaire.
public class Pegasus extends Horse implements Flying
.Réponses:
Vous pouvez créer des interfaces pour les classes d'animaux (classe au sens biologique), comme
public interface Equidae
pour les chevaux etpublic interface Avialae
pour les oiseaux (je ne suis pas biologiste, donc les termes peuvent être faux).Ensuite, vous pouvez toujours créer un
et
et aussi
Ajout à partir des commentaires:
Afin de réduire le code en double, vous pouvez créer une classe abstraite contenant la plupart du code commun des animaux que vous souhaitez implémenter.
Mettre à jour
Je voudrais ajouter un autre détail. Comme Brian le fait remarquer , c'est quelque chose que le PO savait déjà.
Cependant, je tiens à souligner que je suggère de contourner le problème de "multi-héritage" avec les interfaces et que je ne recommande pas d'utiliser des interfaces qui représentent déjà un type concret (comme Bird) mais plutôt un comportement (d'autres se réfèrent à le typage du canard, ce qui est bon aussi, mais je veux dire juste: la classe biologique des oiseaux, Avialae). Je ne recommande pas non plus d'utiliser des noms d'interface commençant par un «I» majuscule, comme
IBird
, qui ne dit simplement rien sur la raison pour laquelle vous avez besoin d'une interface. C'est la différence avec la question: construire la hiérarchie d'héritage à l'aide d'interfaces, utiliser des classes abstraites lorsque cela est utile, implémenter des classes concrètes si nécessaire et utiliser la délégation si nécessaire.la source
AbstractHorse
, qui peut également être utilisé pour construire des zèbres ou d'autres animaux ressemblant à des chevaux.AbstractEquidae
serait probablement plus approprié queAbstractHorse
. Il serait étrange qu'un zèbre étende un cheval abstrait. Bonne réponse, au fait.Duck
implémentation de classeAvialae
?Il existe deux approches fondamentales pour combiner des objets entre eux:
La façon dont cela fonctionne est que vous avez un objet Animal. Dans cet objet, vous ajoutez ensuite d'autres objets qui donnent les propriétés et les comportements dont vous avez besoin.
Par exemple:
Maintenant
IFlier
ressemble à ceci:Alors
Bird
ressemble à ceci:Vous bénéficiez désormais de tous les avantages de l'héritage. Vous pouvez réutiliser le code. Vous pouvez avoir une collection d'IFliers, et pouvez utiliser tous les autres avantages du polymorphisme, etc.
Cependant, vous avez également toute la flexibilité de Composition. Vous pouvez appliquer autant d'interfaces différentes et de classes de support composite que vous le souhaitez à chaque type de
Animal
- avec autant de contrôle que nécessaire sur la configuration de chaque bit.Modèle de stratégie approche alternative à la composition
Une approche alternative en fonction de ce que vous faites et de la manière dont vous faites est de faire en sorte que la
Animal
classe de base contienne une collection interne pour conserver la liste des différents comportements. Dans ce cas, vous finissez par utiliser quelque chose de plus proche du modèle de stratégie. Cela présente des avantages en termes de simplification du code (par exemple,Horse
n'a pas besoin de savoir quoi que ce soit surQuadruped
ouHerbivore
) mais si vous ne faites pas également l'approche d'interface, vous perdez beaucoup des avantages du polymorphisme, etc.la source
getFlier()
doit être réimplémenté pour chaque type d'oiseau.MathsTeacher
et à laEnglishTeacher
fois héritantTeacher
,ChemicalEngineer
,MaterialsEngineer
etc HériterEngineer
.Teacher
et lesEngineer
deux implémententComponent
. LePerson
puis a juste une liste deComponent
s, et vous pouvez leur donner les bonsComponent
pour celaPerson
. à- direperson.getComponent(Teacher.class)
,person.getComponent(MathsTeacher.class)
etc.J'ai une idée stupide:
la source
Puis-je suggérer le concept de Duck-typing ?
Très probablement, vous auriez tendance à faire étendre le Pegasus à une interface Oiseau et Cheval, mais le typage du canard suggère en fait que vous devriez plutôt hériter du comportement . Comme déjà indiqué dans les commentaires, un pégase n'est pas un oiseau mais il peut voler. Donc, votre Pegasus devrait plutôt hériter d'une
Flyable
interface et disons unGallopable
-interface.Ce type de concept est utilisé dans le modèle de stratégie . L'exemple donné vous montre en fait comment un canard hérite du
FlyBehaviour
etQuackBehaviour
et il peut toujours y avoir des canards, par exemple leRubberDuck
, qui ne peuvent pas voler. Ils auraient également pu faire de l'Duck
extension uneBird
classe, mais ils auraient alors renoncé à une certaine flexibilité, car tout le mondeDuck
serait capable de voler, même les pauvresRubberDuck
.la source
Techniquement parlant, vous ne pouvez étendre qu'une classe à la fois et implémenter plusieurs interfaces, mais lorsque vous mettez la main sur l'ingénierie logicielle, je préfère suggérer une solution spécifique à un problème qui ne peut généralement pas répondre. Soit dit en passant, c'est une bonne pratique OO, de ne pas étendre les classes concrètes / seulement d'étendre les classes abstraites pour empêcher un comportement d'héritage indésirable - il n'y a pas de "animal" et pas d'utilisation d'un objet animal mais seulement des animaux concrets.
la source
Dans Java 8, qui est toujours en phase de développement en février 2014, vous pouvez utiliser des méthodes par défaut pour obtenir une sorte d'héritage multiple de type C ++. Vous pouvez également consulter ce didacticiel qui montre quelques exemples avec lesquels il devrait être plus facile de commencer à travailler que la documentation officielle.
la source
Il est prudent de garder un cheval dans une écurie avec une demi-porte, car un cheval ne peut pas franchir une demi-porte. C'est pourquoi j'ai mis en place un service d'hébergement pour chevaux qui accepte tout article de type cheval et le met dans une écurie avec une demi-porte.
Alors, est-ce qu'un cheval comme un animal qui peut voler est même un cheval?
J'avais l'habitude de beaucoup penser à l'héritage multiple, mais maintenant que je programme depuis plus de 15 ans, je ne me soucie plus d'implémenter l'héritage multiple.
Plus souvent qu'autrement, quand j'ai essayé de faire face à une conception qui pointait vers l'héritage multiple, je suis venu plus tard pour publier que j'avais mal compris le domaine du problème.
OU
la source
Java n'a pas de problème d'héritage multiple, car il n'a pas d'héritage multiple. C'est par conception, afin de résoudre le vrai problème d'héritage multiple (le problème du diamant).
Il existe différentes stratégies pour atténuer le problème. Le plus immédiatement réalisable étant l'objet Composite suggéré par Pavel (essentiellement la manière dont C ++ le gère). Je ne sais pas si l'héritage multiple via la linéarisation C3 (ou similaire) est sur les cartes pour l'avenir de Java, mais j'en doute.
Si votre question est théorique, alors la bonne solution est que l'oiseau et le cheval sont plus concrets, et il est faux de supposer qu'un Pegasus est simplement un oiseau et un cheval combinés. Il serait plus correct de dire qu'un Pegasus a certaines propriétés intrinsèques en commun avec les oiseaux et les chevaux (c'est-à-dire qu'ils ont peut-être des ancêtres communs). Cela peut être suffisamment modélisé comme le souligne la réponse de Moritz.
la source
Je pense que cela dépend beaucoup de vos besoins et de la manière dont vos classes d'animaux doivent être utilisées dans votre code.
Si vous souhaitez pouvoir utiliser les méthodes et les fonctionnalités de vos implémentations Horse et Bird dans votre classe Pegasus, vous pouvez implémenter Pegasus en tant que composition d'un oiseau et d'un cheval:
Une autre possibilité est d'utiliser un système Entité-Composant approche au lieu de l'héritage pour définir vos animaux. Bien sûr, cela signifie que vous n'aurez pas de classes Java individuelles des animaux, mais au lieu de cela, elles ne sont définies que par leurs composants.
Un pseudo code pour une approche Entity-Component-System pourrait ressembler à ceci:
la source
vous pouvez avoir une hiérarchie d'interface, puis étendre vos classes à partir des interfaces sélectionnées:
puis définissez vos classes selon vos besoins, en étendant une interface spécifique:
la source
IBird
etIHorse
devrait mettre en œuvre à laIAnimal
place deAnimal
Ehm, votre classe ne peut être la sous-classe que pour 1 autre, mais vous pouvez quand même avoir autant d'interfaces implémentées que vous le souhaitez.
Un Pegasus est en fait un cheval (c'est un cas particulier de cheval), qui est capable de voler (ce qui est la "compétence" de ce cheval spécial). D'un autre côté, vous pouvez dire que le Pegasus est un oiseau, qui peut marcher et qu'il a 4 pattes - tout dépend, comment il est plus facile pour vous d'écrire le code.
Comme dans votre cas, vous pouvez dire:
la source
Les interfaces ne simulent pas l'héritage multiple. Les créateurs de Java considéraient que l'héritage multiple était faux, il n'y a donc rien de tel en Java.
Si vous souhaitez combiner les fonctionnalités de deux classes en une seule, utilisez la composition d'objets. C'est à dire
Et si vous souhaitez exposer certaines méthodes, définissez-les et laissez-les déléguer l'appel au contrôleur correspondant.
Ici, les interfaces peuvent être utiles - si
Component1
implémente l'interfaceInterface1
etComponent2
implémenteInterface2
, vous pouvez définirPour que vous puissiez utiliser des objets de manière interchangeable là où le contexte le permet.
Donc, à mon avis, vous ne pouvez pas entrer dans le problème des diamants.
la source
Comme vous le savez déjà, l'héritage multiple de classes en Java n'est pas possible, mais c'est possible avec des interfaces. Vous pouvez également envisager d'utiliser le modèle de conception de composition.
J'ai écrit un article très complet sur la composition il y a quelques années ...
/codereview/14542/multiple-inheritance-and-composition-with-java-and-c-updated
la source
Jetez un œil à l'exemple ci-dessous pour une meilleure compréhension
Quand utiliser le motif décorateur?
la source
Pour réduire la complexité et simplifier le langage, l'héritage multiple n'est pas pris en charge dans java.
Considérons un scénario où A, B et C sont trois classes. La classe C hérite des classes A et B. Si les classes A et B ont la même méthode et que vous l'appelez à partir d'un objet de classe enfant, il y aura ambiguïté pour appeler la méthode de la classe A ou B.
Étant donné que les erreurs de compilation sont meilleures que les erreurs d'exécution, java génère une erreur de compilation si vous héritez de 2 classes. Donc, que vous ayez la même méthode ou une méthode différente, il y aura maintenant une erreur de compilation.
la source
Pour résoudre le problème de l'héritage multiple en Java → l'interface est utilisée
J2EE (core JAVA) Notes de M. KVR Page 51
la source