67 votes

Pourquoi utiliser l'héritage ?

Je sais que la question a été discuté avant mais il semble toujours que l'héritage soit au moins parfois préférable à la composition. J'aimerais remettre en question cette hypothèse dans l'espoir de mieux comprendre.

Ma question est la suivante : Depuis la composition d'objets permet d'accomplir tout ce que l'on peut accomplir avec l'héritage classique. et puisque l'héritage classique est très souvent malmené [1]. et puisque La composition de l'objet délégué vous donne la flexibilité de changer l'objet délégué en cours d'exécution, pourquoi voudrais-tu jamais utiliser l'héritage classique ?

Je peux en quelque sorte comprendre pourquoi vous recommandez l'héritage dans certains langages comme Java et C++ qui n'offrent pas de syntaxe pratique pour la délégation. Dans ces langages, vous pouvez économiser beaucoup de frappe en utilisant l'héritage chaque fois qu'il n'est pas clairement incorrect de le faire. Mais d'autres langages comme Objective C et Ruby offrent à la fois l'héritage classique et l'héritage par délégation. et syntaxe très pratique pour la délégation. Le langage de programmation Go est le seul langage qui, à ma connaissance, a décidé que l'héritage classique est plus compliqué qu'il n'en vaut la peine et ne supporte que la délégation pour la réutilisation du code.

Une autre façon de formuler ma question est la suivante : Même si vous savez que l'héritage classique n'est pas incorrect pour implémenter un certain modèle, est-ce une raison suffisante pour l'utiliser au lieu de la composition ?

[1] De nombreuses personnes utilisent l'héritage classique pour atteindre le polymorphisme au lieu de laisser leurs classes implémenter une interface. Le but de l'héritage est la réutilisation du code, pas le polymorphisme. De plus, certaines personnes utilisent l'héritage pour modéliser leur compréhension intuitive d'une relation "is-a". ce qui peut souvent poser problème .

Mise à jour

Je veux juste clarifier ce que je veux dire exactement quand je parle d'héritage :

Je parle de le type d'héritage par lequel une classe hérite d'une classe de base partiellement ou totalement implémentée . Je suis pas Vous parlez d'hériter d'une classe de base purement abstraite, ce qui revient à implémenter une interface, ce que, pour mémoire, je ne conteste pas.

Mise à jour 2

Je comprends que l'héritage est le seul moyen de réaliser le polymorphisme en C++. Dans ce cas, la raison pour laquelle vous devez l'utiliser est évidente. Ma question se limite donc à des langages comme Java ou Ruby qui offrent des moyens distincts de réaliser le polymorphisme (interfaces et duck typing, respectivement).

3 votes

"Pourquoi utiliser l'héritage classique ?" D'abord, vous devriez probablement poser la question opposée. Pourquoi n'utiliseriez-vous pas l'héritage classique ? Si un langage offre un concept (l'héritage dans ce cas), l'éviter à cause de "croyances religieuses" ajoute simplement des obstacles inutiles. Il y a toujours plusieurs façons de faire quelque chose. Vous pourriez également poser de nombreuses autres questions similaires - "si vous pouvez programmer en asm, pourquoi auriez-vous besoin du C, et si vous pouvez programmer en C++, pourquoi utiliseriez-vous Python ? Ce n'est qu'un outil parmi d'autres - on l'utilise ou on ne l'utilise pas.

11 votes

@SigTerm De nombreux langages, même modernes, supportent le goto. Une bonne raison pas d'utiliser une fonctionnalité du langage est lorsqu'elle introduit une complexité inutile. J'essaie simplement de comprendre quels sont les avantages - s'il y en a - de l'héritage qui justifieraient la complexité supplémentaire.

0 votes

@KaptajnKold : "goto" J'ai vu des situations où goto était utile. En langage moderne. "La complexité supplémentaire en vaudrait la peine" La complexité supplémentaire apparaît lorsque vous essayez de tout faire en évitant l'héritage. Disons que vous avez une liste de "ressources" quelque part qui doit être traitée de manière spéciale, et que vous voulez ajouter un nouveau type de ressource. Avec l'héritage (multiple), cela peut être fait en quelques lignes de code - vous ajoutez à l'objet les propriétés/comportement de la ressource. En utilisant la composition... disons que ce sera plus compliqué.

53voto

Jerry Coffin Points 237758

[Note : Cette question a été initialement étiquetée comme étant agnostique au niveau du langage. Sur cette base, cette réponse a été écrite pour être relativement agnostique en termes de langage, elle traite donc de l'héritage tel qu'il est utilisé dans un large éventail de langages, tels que Smalltalk, C++ et Object Pascal. Depuis, elle a été modifiée pour traiter spécifiquement de Java. Java est différent en définissant un class et un interface comme deux choses totalement distinctes. L'idée que le but de l'héritage est la réutilisation du code, et non le polymorphisme, est raisonnable d'un point de vue spécifique à Java, mais clairement fausse d'un point de vue agnostique du langage. Si vous ne vous intéressez qu'à Java, ce n'est probablement pas la meilleure réponse].

Le but de l'héritage est la réutilisation du code, pas le polymorphisme.

C'est votre erreur fondamentale. C'est presque exactement le contraire qui est vrai. Le site primaire L'objectif de l'héritage (public) est de modéliser les relations entre les classes en question. Le polymorphisme en est une grande partie.

Lorsqu'il est utilisé correctement, l'héritage ne consiste pas à réutiliser du code existant. Il s'agit plutôt d'être utilisé par code existant. En d'autres termes, si vous avez du code existant qui peut fonctionner avec la classe de base existante, lorsque vous dérivez une nouvelle classe de cette classe de base existante, cet autre code peut maintenant automatiquement fonctionner avec votre nouvelle classe dérivée.

Il est possible d'utiliser l'héritage pour la réutilisation du code, mais si vous le faites, cela doit normalement être privé l'héritage et non l'héritage public. Si le langage que vous utilisez supporte bien la délégation, il y a de fortes chances pour que vous ayez rarement une raison d'utiliser l'héritage privé. Par contre, l'héritage privé supporte certaines choses que la délégation ne supporte pas (normalement). En particulier, même si le polymorphisme est une préoccupation résolument secondaire dans ce cas, il est possible d'utiliser l'héritage privé. peut être un problème - c'est-à-dire qu'avec l'héritage privé, vous pouvez partir d'une classe de base qui est presque ce que vous voulez, et (en supposant qu'il le permette) remplacer les parties qui ne sont pas tout à fait correctes.

Avec la délégation, votre seul véritable choix est d'utiliser la classe existante telle quelle. Si elle ne fait pas tout à fait ce que vous voulez, votre seul choix réel est d'ignorer complètement cette fonctionnalité et de la réimplémenter depuis le début. Dans certains cas, il n'y a pas de perte, mais dans d'autres, la perte est assez importante. Si d'autres parties de la classe de base utilisent la fonction polymorphe, l'héritage privé vous permet de surpasser uniquement la fonction polymorphe, et les autres parties utiliseront votre fonction surchargée. Avec la délégation, vous ne pouvez pas facilement ajouter votre nouvelle fonctionnalité, de sorte que les autres parties de la classe de base existante utiliseront ce que vous avez remplacé.

0 votes

Je pense que dans les langages typés canard, il n'est pas nécessaire d'hériter d'une classe pour pouvoir la remplacer dans un code existant. Une classe qui délègue tout à une autre classe serait très probablement capable de la remplacer.

4 votes

Après avoir réfléchi un peu plus, je pense également que le but de l'héritage est la réutilisation du code et non le polymorphisme. Pour le polymorphisme, il suffit de mettre en œuvre une interface requise, mais avec l'héritage, vous héritez de la mise en œuvre.

0 votes

@Alexey : Oui, si vous parlez spécifiquement des langues spécifiées dans la "Mise à jour 2" du PO (écrite ~6 mois après cette réponse), cela devient une position défendable (c'est-à-dire que je ne suis pas sûr d'être d'accord pour dire que c'est entièrement correct, mais au moins ce n'est pas une absurdité aussi évidente).

19voto

dsimcha Points 32831

Si vous déléguez tout ce que vous n'avez pas explicitement surchargé à un autre objet implémentant la même interface (l'objet "de base"), alors vous avez fondamentalement greenspunned l'héritage par-dessus la composition, mais (dans la plupart des langages) avec beaucoup plus de verbosité et de passe-partout. L'objectif de l'utilisation de la composition au lieu de l'héritage est de ne déléguer que les comportements que vous souhaitez déléguer.

Si vous voulez que l'objet utilise tout le comportement de la classe de base à moins qu'il ne soit explicitement surchargé, alors l'héritage est la manière la plus simple, la moins verbeuse et la plus directe de l'exprimer.

6 votes

Un autre grand avantage de la composition est la possibilité de changer l'objet que vous déléguez au moment de l'exécution. C'est comme si vous héritiez d'un parent, mais que vous pouviez ensuite spécifier un autre parent dont vous héritez au moment de l'exécution. Ainsi, même si vous ne faites que déléguer toutes vos méthodes, il y a toujours un gros avantage. En particulier lors des tests et des simulations.

3 votes

Oui, l'héritage est juste un cas spécial bizarre de composition qui fixe de manière déraisonnable l'élément interne. Il y a probablement un cas dans lequel l'héritage pourrait vous faire économiser quelques frappes, mais cela ne justifie pas toute la confusion et les utilisations inappropriées qu'il a créées.

2 votes

Au cas où le mot "Greenspunned" ci-dessus ne vous serait pas familier, il s'agit d'amener une fonctionnalité d'une autre langue dans la vôtre, mais en l'implémentant mal. Voir La dixième règle de Greenspun y Abstractions fuyantes et verdoyantes .

19voto

La principale raison de l'utilisation de l'héritage est pas comme une forme de composition - c'est pour que vous puissiez obtenir un comportement polymorphe. Si vous n'avez pas besoin de polymorphisme, vous ne devriez probablement pas utiliser l'héritage, du moins en C++.

7voto

faisalbhagat Points 396

Tout le monde sait que le polymorphisme est un grand avantage de l'héritage. Un autre avantage que je trouve dans l'héritage est qu'il aide à créer une réplique du monde réel. Par exemple, dans le système des fiches de paie, nous traitons avec des managers, des développeurs, des employés de bureau, etc. Si nous héritons de toutes ces classes avec la super classe Employé, cela rend notre programme plus compréhensible dans le contexte du monde réel où toutes ces classes sont essentiellement des employés. Et encore une chose, les classes ne contiennent pas seulement des méthodes mais aussi des attributs. Ainsi, si nous contenons des attributs génériques à l'employé dans la classe Employee comme le numéro de sécurité sociale, l'âge, etc., cela permettrait une meilleure réutilisation du code, une plus grande clarté conceptuelle et, bien sûr, le polymorphisme. Cependant, lorsque nous utilisons l'héritage, nous devons garder à l'esprit le principe de conception de base "Identifier les aspects de votre application qui varient et les séparer des aspects qui changent". Vous ne devriez jamais implémenter ces aspects de l'application qui changent par héritage, mais plutôt utiliser la composition. Et pour les aspects qui ne sont pas modifiables, vous devriez utiliser l'héritage, bien sûr, si une relation évidente "est un" existe.

6voto

Aaron Digulla Points 143830

L'héritage est à privilégier si :

  1. Vous devez exposer l'API complète de la classe que vous étendez (avec la délégation, vous devrez écrire beaucoup de méthodes de délégation). et votre langage n'offre pas un moyen simple de dire "déléguer toutes les méthodes inconnues à".
  2. Vous devez accéder à des champs/méthodes protégés pour des langues qui n'ont pas le concept d'"amis".
  3. Les avantages de la délégation sont quelque peu réduits si votre langage permet la multi-héritage.
  4. Vous n'avez généralement pas besoin de délégation du tout si votre langage permet d'hériter dynamiquement d'une classe ou même d'une instance au moment de l'exécution. Vous n'en avez pas du tout besoin si vous pouvez contrôler quelles méthodes sont exposées (et comment elles le sont) en même temps.

Ma conclusion : La délégation est une solution de contournement pour un bug dans un langage de programmation.

0 votes

Re 1) C'est pourquoi j'ai écrit que l'héritage a un sens dans certains langages, notamment ceux qui rendent la délégation très verbeuse. Re 2) C'est possible. Mais il faudrait travailler dur pour me convaincre qu'un tel besoin n'est pas un défaut de conception au départ. Re 3) Je ne vois pas pourquoi. Re 4) Cela a plus à voir avec le fait de surmonter les limites de l'héritage, ce qui démontre mon point de vue. L'héritage est limitatif et difficile à mettre en place. La délégation ne semble pas avoir les mêmes problèmes.

1 votes

Les langages comme Java rendent la délégation difficile, c'est pourquoi l'héritage est très souvent utilisé, même si la délégation aurait été préférable.

0 votes

Êtes-vous donc d'accord pour dire que l'héritage dans les langages qui facilitent la délégation (par exemple l'Objective C) est plus difficile qu'il ne vaut la peine ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X