J'ai un code où un bon modèle d'héritage s'est dégradé et j'essaie de comprendre pourquoi et comment y remédier. Fondamentalement, imaginez que vous ayez une hiérarchie Zoo avec:
class Animal
class Parrot : Animal
class Elephant : Animal
class Cow : Animal
etc.
Vous avez vos méthodes eat (), run (), etc. et tout va bien. Puis un jour, quelqu'un arrive et dit - notre classe CageBuilder fonctionne très bien et utilise les animaux.weight () et animal.height (), à l'exception du nouveau bison d'Afrique qui est trop fort et peut briser le mur, donc je vais ajouter une propriété de plus pour la classe Animal - isAfricanBizon () et utilisez-la lors du choix du matériau et remplacez-la uniquement pour la classe AfricanBizon. La personne suivante vient et fait quelque chose de similaire et la prochaine chose que vous savez, c'est que toutes ces propriétés sont spécifiques à un sous-ensemble de la hiérarchie dans la classe de base.
Quelle est la bonne façon d'améliorer / refactoriser un tel code? Une alternative ici serait d'utiliser simplement dynamic_casts pour vérifier les types mais cela encombre les appelants et ajoute un tas de if-then-else partout. Vous pouvez avoir des interfaces plus spécifiques ici, mais si tout ce que vous avez est la référence de classe de base qui n'aide pas beaucoup non plus. D'autres suggestions? Exemples?
Merci!
la source
Réponses:
Il semble que le problème soit au lieu d'implémenter requiresConcreteWall (), ils ont implémenté un indicateur d'appel IsAfricanBison (), puis ont déplacé la logique pour savoir si le mur devait ou non changer en dehors de la portée de la classe. Vos cours doivent exposer le comportement et les exigences, pas l'identité; vos consommateurs de ces classes devraient travailler à partir de ce qu'on leur dit, et non en fonction de ce qu'ils sont.
la source
isAfricanBizon () n'est pas générique. Supposons que vous étendez votre ferme d'animaux avec un hyppopotame qui est également trop fort, mais que retourner vrai depuis isAfricanBizon () pour avoir le bon effet serait tout simplement idiot.
vous souhaitez toujours ajouter des méthodes à l'interface qui répondent à la question spécifique, dans ce cas, ce serait quelque chose comme la force ()
la source
strength
méthode pourrait être interrogéematerial.canHold(animal)
, permettant une manière propre de supporter différents types de matériauxConcreteWall
.Je pense que votre problème est le suivant: vous avez divers clients de la bibliothèque qui ne sont intéressés que par un sous-ensemble de la hiérarchie mais qui reçoivent un pointeur / référence vers la classe de base. C'est en fait le problème que dynamic_cast <> est là pour résoudre.
C'est une question de conception des clients pour minimiser l'utilisation de dynamic_cast <>; ils doivent l'utiliser pour déterminer si l'objet nécessite un traitement spécial et, dans l'affirmative, effectuer toutes les opérations sur la référence convertie.
Si vous disposez de collections de fonctionnalités de type "mix-in" qui s'appliquent à plusieurs sous-hiérarchies distinctes, vous pouvez utiliser le modèle d'interface utilisé par Java et C #; avoir une classe de base virtuelle qui est une classe virtuelle pure et utiliser dynamic_cast <> pour déterminer si une instance fournit une implémentation pour elle.
la source
Une chose que vous pouvez faire est de remplacer la vérification explicite de type comme
isAfricanBison()
par la vérification des propriétés qui vous intéressent réellement, c'est-à-direisTooStrong()
.la source
Les animaux ne devraient pas se soucier des murs en béton. Vous pouvez peut-être l'exprimer avec des valeurs simples.
Je soupçonne que ce n'est pas viable cependant. C'est le problème avec les exemples de jouets, cependant.
Je ne voudrais jamais voir des RequestsConcreteWalls () ou des lignes et des lignes de pointeurs dynamiques en tout cas.
Il s'agit généralement d'une solution bon marché . C'est facile à entretenir et à conceptualiser. Et vraiment, le problème déclare que son lié au type animal de toute façon.
Cela ne vous empêche pas non plus d'utiliser du code partagé, pollue juste un peu Animal.
Mais la façon dont la cage est construite peut être une politique d'un autre système, et vous avez peut-être plus d'un type de constructeur de cage par animal. Il existe de nombreuses combinaisons étranges et alambiquées.
J'ai utilisé la conception basée sur les composants à de bonnes fins, le principal problème est que cela peut être gênant lorsque la propriété d'Animal est partagée. Comment éviter de jeter des destructeurs étant le point douloureux.
Double Dispatch est une autre option, même si j'ai toujours été réticent à y sauter.
Au-delà de cela, il est difficile de deviner le problème.
la source
Bien sûr, tous les animaux ont la propriété inhérente de
attemptEscape()
. Alors que certains la méthode peut poser unfalse
résultat dans tous les scénarios tandis que d'autres peuvent avoir une chance basée sur l'heuristique de leurs autres caractéristiques intrinsèques telles quesize
etweight
. Puis, à un moment donné, celaattemptEscape()
devient trivial, car il reviendra très certainementtrue
.J'ai bien peur de ne pas comprendre complètement votre question ... tous les animaux ont des actions et des caractéristiques liées. Ceux spécifiques à l'animal doivent être introduits là où il convient. Essayer de relier directement Bison à Parrots n'est pas une bonne configuration d'hérédité et ne devrait vraiment pas être un problème dans une conception appropriée.
la source
Une autre option serait d'utiliser une usine qui crée des cages adaptées à chaque animal. Je pense que cela peut être mieux dans le cas où les conditions sont très différentes pour chacun d'eux. Mais si c'est juste cette condition, la
RequiresConcreteWall()
méthode mentionnée ci-dessus le fera.la source
que diriez-vous de recommanderCageType () comme oppsed à requiertConcreteWall ()
la source
Pourquoi ne pas faire quelque chose comme ça
class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison
Avec la classe HeavyAnimals, vous pouvez créer la classe African Bison en étendant la classe HeavyAnimals.
Alors maintenant, vous la classe parente (les animaux) qui peut être utilisée pour créer une autre classe de base comme la classe HeavyAnimal avec peut être utilisée pour créer la classe du bison africain et d'autres animaux lourds. Donc, avec le bison d'Afrique, vous avez maintenant accès aux méthodes et aux propriétés de la classe Animal (c'est la base pour tous les animaux) et l'accès à la classe HeavyAnimals (c'est la base pour les animaux lourds)
la source