Pourquoi le sous-classement est-il trop mauvais (et donc pourquoi devrions-nous utiliser des prototypes pour le supprimer)?

10

Je lisais sur les modèles de conception et j'ai lu que le modèle de conception du prototype supprime le sous-classement excessif.

Pourquoi le sous-classement est-il mauvais? Quel avantage l'utilisation d'un prototype entraînerait-elle par rapport au sous-classement?

David Faux
la source
dit qui ? avez vous un lien ?
camus
Je lisais la p.118 de ce livre. goo.gl/6JGw1
David Faux

Réponses:

19

Pourquoi le sous-classement est-il trop mauvais

«Trop» est un appel au jugement; mais prenez-le moi c'est mauvais. Le code avec lequel je travaille a l'héritage DEEP et le code est sacrément difficile à lire, à comprendre, à suivre, à tracer, à déboguer, etc. etc. Il est effectivement impossible d'écrire du code de test pour ce genre de choses.

Le modèle de prototype supprime le sous-classement

Ou la question signifie-t-elle "supprime trop de sous-classes?". Ce modèle nécessite le clonage d'un objet "modèle" pour éviter le sous-classement, au moins à ce stade. Aucune règle ne dit que le "modèle" ne peut pas être une sous-classe.

Privilégier la composition à l'héritage

Cette idée comprend également la délégation et l'agrégation. Suivre cette heuristique signifie que votre logiciel a tendance à être plus flexible, plus facile à maintenir, à étendre et à réutiliser.

Lorsqu'une classe est composée de parties, vous pouvez les remplacer au moment de l'exécution . Cela a un effet profond sur la testabilité.

Les tests sont plus faciles . Vous pouvez utiliser de fausses pièces (c'est-à-dire des "simulacres", des "doubles" et d'autres tests de conversation). L'héritage profond de notre code signifie que nous devons instancier toute la hiérarchie pour en tester n'importe quel morceau. Dans notre cas, cela n'est pas possible sans exécuter le code dans son environnement réel. Par exemple, nous avons besoin d'une base de données pour instancier des objets métier.

Les changements s'accompagnent d'effets secondaires et d'incertitude - Plus la classe est «fondée», plus les effets sont répandus, pour le meilleur ou pour le pire. Il y a peut-être des changements souhaités que vous n'oserez pas faire en raison de l'incertitude des effets secondaires. Ou un changement qui est bon pour un endroit de notre chaîne d'héritage est mauvais pour un autre. C'est certainement mon expérience.

radarbob
la source
Je serais vraiment intéressé de voir quelques exemples de cas où l'héritage rend les tests (en particulier les moqueries) difficiles et comment la composition aide à cela.
Armand
@alison: une méthode dans une classe dérivée qui utilise une autre méthode de la classe de base où l'implémentation dans la classe de base rend pratiquement impossible de tester des scénarios d'erreur. Si cela avait été la composition, il serait plus facile d'injecter un objet à l'origine de l'erreur
Rune FS
@allison: Exemples? Lorsque la base de code est très grande, lorsque les classes de base sont utilisées directement par des dizaines de classes et des centaines par héritage. Parce que le point d'héritage profond est la réutilisation et donc une classe de base est générique au point que la trace de la pile du débogueur ne peut pas montrer quelles méthodes sont réellement appelées. Enfin, lorsqu'il n'y a pas d'injection de dépendance déjà intégrée dans un tel Frankenstein. Une classe de niveau supérieur peut "être" une demi-douzaine ou plus d'autres choses qui, collectivement, "ont" des dizaines d'objets internes - chacun avec sa propre maison d'amusement d'héritage.
radarbob
8

Je pense que l'une des raisons pour lesquelles cela a été considéré comme une mauvaise pratique est qu'au fil du temps, chaque sous-classe peut contenir des attributs et des méthodes qui n'ont aucun sens pour cet objet plus vous descendez la chaîne (dans une hiérarchie peu développée). Vous pouvez vous retrouver avec une classe gonflée qui contient des éléments qui n'ont pas de sens pour cette classe.

Je suis agnostique à ce sujet moi-même. Il semble dogmatique de dire que c'est mauvais. Tout est mauvais lorsqu'il est mal utilisé.

jmq
la source
2

Deux raisons.

  1. Est la performance d'exécution. Chaque fois que vous appelez une sous-classe à partir d'une super-classe, vous effectuez un appel de méthode, donc si vous avez imbriqué cinq classes et invoqué une méthode dans la classe de base, vous effectuerez cinq appels de méthode. La plupart des compilateurs / runtimes essaieront de reconnaître cela et d'optimiser les appels supplémentaires, mais cela n'est sûr que pour des cas très simples.

  2. Est la raison de vos collègues programmeurs. Si vous imbriquez cinq classes, je dois examiner les quatre classes parentes pour établir que vous appelez vraiment une méthode dans la classe de base, cela devient fastidieux. Je devrai peut-être également parcourir cinq appels de méthode lorsque je déboguerai le programme.

James Anderson
la source
6
-1: Vous avez raison sur le # 2; mais pas sur # 1. Plusieurs couches d'héritage n'affecteront pas nécessairement les performances d'exécution.
Jim G.
Vous vous inquiétez de quelques appels de procédure supplémentaires au niveau du code machine et en utilisant OP et l'héritage, et vous vous inquiétez de la santé mentale de vos collègues programmeurs .....
mattnz
@JimG. Ce n'est peut-être pas le cas, mais c'est possible. :) Il y a aussi une utilisation possible de la mémoire à se soucier: si vous ne réutilisez pas toutes les variables de la base, elles peuvent toujours être allouées dans le cadre de la construction de l'objet.
Adam Lear
2

«Trop» est un appel au jugement.

Comme pour la plupart des choses en programmation, la sous-classification n'est pas intrinsèquement mauvaise lorsqu'elle a du sens. Lorsque le modèle de votre solution au problème a plus de sens en tant que hiérarchie plutôt qu'en tant que composition de pièces, le sous-classement peut être l'option préférée.

Cela dit, privilégier la composition plutôt que l'héritage est une bonne règle de base.

Lorsque vous sous-classe, les tests peuvent être plus difficiles (comme l' a noté radarbob ). Dans une hiérarchie profonde, l'isolement du code problématique est plus difficile car l'instanciation d'une sous-classe nécessite d'intégrer la hiérarchie de la superclasse entière et un tas de code supplémentaire. Avec une approche compositionnelle, vous pouvez isoler et tester chaque composant de votre objet agrégé avec des tests unitaires, des objets factices, etc.

Le sous-classement peut également vous amener à exposer des détails de votre implémentation que vous ne souhaitez peut-être pas. Cela rend difficile le contrôle de l'accès à la représentation sous-jacente de vos données et vous lie à une implémentation particulière de votre modèle de sauvegarde.

Un exemple auquel je peux penser est java.util.Properties, qui hérite de Hashtable. La classe Properties est uniquement destinée à stocker des paires String-String (cela est noté dans les Javadocs). En raison de l'héritage, les méthodes put et putAll de Hashtable ont été exposées aux utilisateurs des propriétés. Bien que la "bonne" façon de stocker une propriété soit avec setProperty (), rien n'empêche un utilisateur d'appeler put () et de passer une chaîne et un objet.

Fondamentalement, la sémantique des propriétés est mal définie en raison de l'héritage. En outre, si quelqu'un souhaite modifier l'objet de support pour les propriétés, l'impact aura un impact beaucoup plus important sur le code qui utilise les propriétés que si les propriétés avaient adopté une approche plus compositionnelle.

seggy
la source
2

L'héritage peut rompre l'encapsulation dans certains cas, et si cela se produit, c'est mauvais. Lisez pour en savoir plus.


Dans le bon vieux temps sans héritage, une bibliothèque publiait une API et une application l'utilisant utilisait ce qui était publiquement visible, et la bibliothèque avait maintenant sa propre façon de gérer les engrenages internes qui changeaient sans casser l'application.

À mesure que les besoins évoluent, la bibliothèque peut évoluer et peut avoir plus de clients; ou il peut fournir des fonctionnalités étendues en héritant de sa propre classe avec d'autres saveurs. Jusqu'ici tout va bien.

Cependant, la chose fondamentale est que si une application prend la classe et commence à la sous-classer, maintenant une sous-classe est en fait un client plutôt qu'un partenaire interne. Cependant, contrairement à une manière claire et non ambiguë de définir l'API publique, la sous-classe est maintenant beaucoup plus profonde à l'intérieur de la bibliothèque (accès à toutes les variables privées, etc.). Ce n'est pas encore mal, mais un méchant personnage peut combler une API qui est trop à l'intérieur de l'APP ainsi que dans la bibliothèque-

Essentiellement, ce n'est pas toujours le cas, mais,

Il est facile de mal utiliser l'héritage pour rompre l'encapsulation

Autoriser le sous-classement peut être faux si ce point.

Dipan Mehta
la source
1

Réponse rapide courte:

Cela dépend de ce que vous faites.

Réponse ennuyeuse étendue:

Un développeur peut créer une application. en utilisant des modèles de sous-classement ou (prototypiques). Parfois, une technique fonctionne mieux. Parfois, l'autre technologie fonctionne mieux.

Choisir avec la technique fonctionne mieux, nécessite également de considérer si un scénario "héritage multiple" est présent. Même si le langage de programmation ne prend en charge que l'héritage unique, les modèles ou les prototypes.

Note complémentaire:

Certaines réponses concernent «l'héritage multiple». Bien que ce ne soit pas la question principale, c'est lié au sujet. Il existe plusieurs articles similaires sur «l'héritage multiple par rapport à la composition». J'ai eu un cas où je mélange les deux.

umlcat
la source