Raisonner si l'héritage (ou n'importe quelle caractéristique unique, vraiment) est nécessaire ou non sans considérer également le reste de la sémantique du langage est inutile; vous vous disputez dans le vide.
Ce dont vous avez besoin, c'est d'une philosophie de conception de langage cohérente; la langue doit pouvoir résoudre avec élégance les problèmes pour lesquels elle est conçue. Le modèle pour y parvenir peut ou non nécessiter un héritage, mais il est difficile de juger cela sans une vue d'ensemble.
Si, par exemple, votre langage a des fonctions de première classe, une application de fonction partielle, des types de données polymorphes, des variables de type et des types génériques, vous avez à peu près couvert les mêmes bases que vous auriez avec l'héritage OOP classique, mais en utilisant un paradigme différent.
Si vous avez une liaison tardive, un typage dynamique, des méthodes en tant que propriétés, des arguments de fonction flexibles et des fonctions de première classe, vous couvrirez également les mêmes motifs, mais encore une fois, en utilisant un paradigme différent.
(Trouver des exemples pour les deux paradigmes décrits est laissé comme un exercice pour le lecteur.)
Alors, pensez au type de sémantique que vous voulez, jouez avec eux et voyez si elles sont suffisantes sans héritage. Si ce n'est pas le cas, vous pouvez soit décider de jeter l'héritage dans le mix, soit décider qu'il manque quelque chose d'autre.
Oui, c'est une décision de conception parfaitement raisonnable d'omettre l'héritage.
Il existe en fait de très bonnes raisons de supprimer l'héritage de l'implémentation, car cela peut produire du code extrêmement complexe et difficile à maintenir. J'irais même jusqu'à considérer l'héritage (car il est généralement implémenté dans la plupart des langages POO) comme une erreur.
Clojure, par exemple, ne fournit pas d'héritage d'implémentation, préférant fournir un ensemble de fonctionnalités orthogonales (protocoles, données, fonctions, macros) qui peuvent être utilisées pour obtenir les mêmes résultats mais de manière beaucoup plus propre.
Voici une vidéo que j'ai trouvée très éclairante sur ce sujet général, où Rich Hickey identifie les sources fondamentales de complexité dans les langages de programmation (y compris l'héritage) et présente des alternatives pour chacun: Simple made easy
la source
Lorsque j'ai rencontré pour la première fois le fait que les classes VB6 ne prennent pas en charge l'héritage (uniquement les interfaces), cela m'a vraiment énervé (et le fait toujours).
Cependant, la raison pour laquelle c'est si mauvais, c'est qu'il n'avait pas non plus de paramètres constructeur, donc vous ne pouviez pas faire d'injection de dépendance normale (DI). Si vous avez une DI, c'est plus important que l'héritage, car vous pouvez suivre le principe de privilégier la composition à l'héritage. C'est de toute façon une meilleure façon de réutiliser le code.
Vous n'avez pas de Mixins? Si vous souhaitez implémenter une interface et déléguer tout le travail de cette interface à un ensemble d'objets via l'injection de dépendances, alors les mixins sont idéaux. Sinon, vous devez écrire tout le code passe-partout pour déléguer chaque méthode et / ou propriété à l'objet enfant. Je le fais beaucoup (grâce à C #) et c'est une chose que j'aurais aimé ne pas avoir à faire.
la source
Pour être en désaccord avec une autre réponse: non, vous jetez des fonctionnalités lorsqu'elles sont incompatibles avec quelque chose que vous voulez plus. Java (et d'autres langages GC'd) ont supprimé la gestion explicite de la mémoire car il souhaitait davantage la sécurité des types. Haskell a rejeté la mutation parce qu'il voulait davantage de raisonnement équationnel et de types fantaisistes. Même C a supprimé (ou déclaré illégal) certains types d'alias et d'autres comportements, car il souhaitait davantage d'optimisations du compilateur.
La question est donc la suivante: que voulez-vous de plus que l'héritage?
la source
free
/delete
vous donne la possibilité d'invalider les références; à moins que votre système de saisie ne soit capable de suivre toutes les références affectées, cela rend la langue dangereuse. En particulier, ni C ni C ++ ne sont de type sécurisé. Il est vrai que vous pouvez faire des compromis dans l'un ou l'autre (par exemple, les types linéaires ou les restrictions d'allocation) pour les faire accepter. Pour être précis, j'aurais dû dire que Java voulait une sécurité de type avec un système de type simple et particulier et une allocation sans restriction davantage.null
références). L'interdictionfree
est une restriction supplémentaire assez arbitraire. En résumé, la sécurité d'une langue dépend plus de votre définition de la sécurité des types que de la langue.T
référence se réfère à l'unnull
ou à un objet d'une classe s'étendantT
;null
est moche, mais les opérations surnull
lever une exception bien définie plutôt que de corrompre le type invariant ci-dessus. Contrairement à C ++: après un appel àdelete
, unT*
pointeur peut pointer vers la mémoire qui ne contient plus d'T
objet. Pire encore, si vous effectuez une affectation de champ à l'aide de ce pointeur dans une affectation, vous pouvez mettre à jour entièrement un champ d'un objet d'une classe différente s'il venait à être placé à une adresse proche. Ce n'est pas la sécurité de type par une définition utile du terme.Non.
Si vous souhaitez supprimer une fonctionnalité du langage de base, vous déclarez universellement qu'elle n'est jamais, jamais nécessaire (ou inutilement diffficile à mettre en œuvre, ce qui ne s'applique pas ici). Et «jamais» est un mot fort en génie logiciel. Vous auriez besoin d'une justification très puissante pour faire une telle déclaration.
la source
Parlant d'une perspective strictement C ++, l'héritage est utile à deux fins principales:
Pour le point 1, tant que vous avez un moyen de partager du code sans avoir à vous livrer à trop d'acrobaties, vous pouvez supprimer l'héritage. Pour le point 2. Vous pouvez suivre la voie Java et insister sur une interface pour implémenter cette fonctionnalité.
Les avantages de la suppression de l'héritage sont
Le compromis est en grande partie entre «Ne vous répétez pas» et la flexibilité d'un côté et la prévention des problèmes de l'autre côté. Personnellement, je détesterais voir l'héritage passer de C ++ simplement parce qu'un autre développeur peut ne pas être assez intelligent pour prévoir les problèmes.
la source
Vous pouvez faire beaucoup de travaux très utiles sans héritage d' implémentation ou mixins, mais je me demande si vous devriez avoir une sorte d'héritage d'interface, c'est-à-dire une déclaration qui dit que si un objet implémente l'interface A, il doit également interfacer B (c'est-à-dire, A est une spécialisation de B et il existe donc une relation de type). D'un autre côté, votre objet résultant n'a besoin que d'enregistrer qu'il implémente les deux interfaces, il n'y a donc pas trop de complexité. Tout est parfaitement réalisable.
Le manque d'héritage d'implémentation a cependant un inconvénient clair: vous ne pourrez pas construire de tables vtables indexées numériquement pour vos classes et devrez donc faire des recherches de hachage pour chaque appel de méthode (ou trouver un moyen intelligent de les éviter). Cela pourrait être pénible si vous routez même des valeurs fondamentales (par exemple, des nombres) via ce mécanisme. Même une très bonne implémentation de hachage peut être coûteuse lorsque vous la frappez plusieurs fois dans chaque boucle interne!
la source