Lorsque vous utilisez le concept de polymorphisme, vous créez une hiérarchie de classes et en utilisant la référence des parents, vous appelez les fonctions d'interface sans savoir quel type spécifique a l'objet. C'est bien. Exemple:
Vous avez une collection d'animaux et vous faites appel à la fonction de tous les animaux eat
et peu vous importe si c'est un chien qui mange ou un chat. Mais dans la même hiérarchie de classes, vous avez des animaux qui en ont d'autres - autres que ceux hérités et implémentés de la classe Animal
, par exemple makeEggs
, getBackFromTheFreezedState
etc. Donc, dans certains cas, dans votre fonction, vous voudrez peut-être connaître le type spécifique pour appeler des comportements supplémentaires.
Par exemple, au cas où il est l'heure du matin et si ce n'est qu'un animal, alors vous appelez eat
, sinon si c'est un humain, alors appelez d'abord washHands
, getDressed
et ensuite seulement appelez eat
. Comment gérer ces cas? Le polymorphisme meurt. Vous devez trouver le type d'objet, qui ressemble à une odeur de code. Existe-t-il une approche commune pour gérer ces cas?
Eater
interface avec laeat()
méthode, alors en tant que client, vous ne vous souciez pas qu'uneHuman
implémentation qu'elle doive d'abord appelerwashHands()
etgetDressed()
, c'est un détail d'implémentation de cette classe. Si, en tant que client, vous vous souciez de ce fait, vous n'utilisez probablement pas le bon outil pour le travail.getDressed
avanteat
, ce n'est pas le cas pour le déjeuner. Selon votre situation, celawashHands();if !dressed then getDressed();[code to actually eat]
pourrait être le meilleur moyen de mettre en œuvre cela pour un humain. Une autre possibilité est que faire si d'autres choses l'exigentwashHands
et / ougetDressed
sont appelées? Supposez que vous l'ayezleaveForWork
? Vous devrez peut-être structurer le flux de votre programme pour qu'il soit tel qu'il soit appelé bien avant cela de toute façon.Réponses:
Dépend. Malheureusement, il n'y a pas de solution générique. Pensez à vos besoins et essayez de comprendre ce que ces choses devraient faire.
Par exemple, vous avez dit le matin que différents animaux faisaient des choses différentes. Que diriez - vous vous présenter une méthode
getUp()
ouprepareForDay()
quelque chose comme ça. Ensuite, vous pouvez continuer avec le polymorphisme et laisser chaque animal exécuter sa routine matinale.Si vous souhaitez différencier les animaux, vous ne devez pas les stocker sans distinction dans une liste.
Si rien d'autre ne fonctionne, vous pouvez essayer le modèle de visiteur , qui est une sorte de hack pour permettre une sorte de répartition dynamique où vous pouvez soumettre un visiteur qui recevra des rappels de type exact des animaux. J'insiste cependant sur le fait que cela devrait être un dernier recours si tout le reste échoue.
la source
C'est une bonne question et c'est le genre de problème que beaucoup de gens essaient de comprendre comment utiliser OO. Je pense que la plupart des développeurs ont du mal avec cela. J'aurais aimé pouvoir dire que la plupart l'ont dépassé, mais je ne suis pas sûr que ce soit le cas. La plupart des développeurs, d'après mon expérience, finissent par utiliser des sacs de propriétés pseudo-OO .
Tout d'abord, permettez-moi d'être clair. Ce n'est pas ta faute. La façon dont OO est généralement enseignée est très imparfaite. L'
Animal
exemple est le premier délinquant, l'OMI. Fondamentalement, nous disons, parlons des objets, que peuvent-ils faire. UneAnimal
boîteeat()
et ça peutspeak()
. Super. Créez maintenant des animaux et codez comment ils mangent et parlent. Maintenant, vous savez OO, non?Le problème est que cela arrive à OO dans la mauvaise direction. Pourquoi y a-t-il des animaux dans ce programme et pourquoi ont-ils besoin de parler et de manger?
J'ai du mal à penser à une utilisation réelle d'un
Animal
type. Je suis sûr qu'il existe, mais discutons de quelque chose qui, à mon avis, est plus facile à raisonner: une simulation du trafic. Supposons que nous voulons modéliser le trafic dans divers scénarios. Voici quelques éléments de base dont nous avons besoin pour pouvoir le faire.Nous pouvons aller plus loin avec toutes sortes de choses pour les piétons et les trains, mais nous resterons simples.
Voyons
Vehicle
. De quelles capacités le véhicule a-t-il besoin? Il doit voyager sur une route. Il doit pouvoir s'arrêter aux signaux. Il doit pouvoir naviguer dans les intersections.C'est probablement trop simple mais c'est un début. Maintenant. Et toutes les autres choses qu'un véhicule pourrait faire? Ils peuvent quitter une route et devenir un fossé. Cela fait-il partie de la simulation? Non, je n'en ai pas besoin. Certaines voitures et autobus ont un système hydraulique qui leur permet de rebondir ou de s'agenouiller respectivement. Cela fait-il partie de la simulation? Non, je n'en ai pas besoin. La plupart des voitures brûlent de l'essence. Certains non. La centrale électrique fait-elle partie de la simulation? Non, je n'en ai pas besoin. Taille de roue? Je n'en ai pas besoin. Navigation GPS? Système d'infodivertissement? Je n'en ai pas besoin.
Il vous suffit de définir les comportements que vous allez utiliser. À cette fin, je pense qu'il est souvent préférable de créer des interfaces OO à partir du code qui interagit avec elles. Vous commencez avec une interface vide, puis commencez à écrire le code qui appelle les méthodes inexistantes. C'est ainsi que vous savez de quelles méthodes vous avez besoin sur votre interface. Ensuite, une fois que vous avez fait cela, vous allez commencer à définir des classes qui implémentent ces comportements. Les comportements non utilisés ne sont pas pertinents et n'ont pas besoin d'être définis.
L'intérêt de OO est que vous pouvez ajouter de nouvelles implémentations de ces interfaces plus tard sans changer le code appelant. La seule façon de fonctionner est si les besoins du code appelant déterminent ce qui se passe dans l'interface. Il n'y a aucun moyen de définir tous les comportements de toutes les choses possibles qui pourraient être imaginées plus tard.
la source
TL; DR:
Pensez à une abstraction et à des méthodes qui s'appliquent à toutes les sous-classes et couvrent tout ce dont vous avez besoin.
Restons d'abord avec votre
eat()
exemple.C'est une propriété d'être un humain qui, comme condition préalable à l'alimentation, veut se laver les mains et s'habiller avant de manger. Si vous voulez que quelqu'un vienne prendre votre petit déjeuner avec vous, vous ne leur dites pas de se laver les mains et de s'habiller, ils le font seuls quand vous les invitez, ou ils répondent "Non, je ne peux pas venir fini, je ne me suis pas lavé les mains et je ne suis pas encore habillé ".
Retour au logiciel:
En
Human
exemple ne mange pas sans conditions préalables, j'aurais laHuman
de »eat()
méthode fairewashHands()
etgetDressed()
si cela n'a pas été fait. Ce ne devrait pas être votre travail en tanteat()
qu'appelant de connaître cette particularité. L'alternative de l'homme têtu serait de lever une exception ("Je ne suis pas prêt à manger!") Si les conditions préalables ne sont pas remplies, vous laissant frustré, mais au moins informé que manger n'a pas fonctionné.Et alors
makeEggs()
?Je recommanderais de changer votre façon de penser. Vous voulez probablement exécuter les tâches matinales prévues de tous les êtres. Encore une fois, en tant qu'appelant, ce ne devrait pas être votre travail de savoir quelles sont leurs fonctions. Je recommanderais donc une
doMorningDuties()
méthode que toutes les classes implémentent.la source
La réponse est assez simple.
Vous n'avez pas besoin de le gérer car cela ne servirait à rien. Une interface est généralement conçue en fonction de la façon dont elle va être utilisée. Si votre interface ne définit pas le lavage des mains, alors vous ne vous en souciez pas en tant qu'appelant d'interface; si vous l'aviez fait, vous l'auriez conçu différemment.
Par exemple, en pseudocode:
Maintenant, vous implémentez
IMorningPerformer
pourAnimal
simplement effectuer des repas, et pourHuman
vous également pour vous laver les mains et vous habiller. L'appelant de votre méthode MorningTime peut s'en moquer s'il est humain ou animal. Tout ce qu'il veut, c'est la routine matinale effectuée, ce que chaque objet fait admirablement grâce à OO.Ou alors?
Pourquoi supposez-vous cela? Je pense que cela pourrait être une fausse hypothèse.
Oui, il est généralement résolu avec une hiérarchie de classe ou d'interface soigneusement conçue. Notez que dans l'exemple ci-dessus, rien ne contredit votre exemple tel que vous l'avez donné, mais vous vous sentirez probablement insatisfait, car vous avez fait plus d'hypothèses que vous n'avez pas écrit dans la question au moment de l'écriture. , et ces hypothèses sont probablement violées.
Il est possible d'aller dans un terrier de lapin en resserrant vos hypothèses et en modifiant la réponse pour toujours les satisfaire, mais je ne pense pas que ce serait utile.
La conception de bonnes hiérarchies de classes est difficile et nécessite beaucoup d'informations sur votre domaine d'activité. Pour les domaines complexes, on passe sur deux, trois ou même plusieurs itérations, car ils affinent leur compréhension de la façon dont les différentes entités de leur domaine d'activité interagissent, jusqu'à ce qu'ils arrivent à un modèle adéquat.
C'est là que les exemples d'animaux simplistes font défaut. Nous voulons enseigner de manière simple, mais le problème que nous essayons de résoudre n'est pas évident tant que vous n'approfondissez pas, c'est-à-dire que vous avez des considérations et des domaines plus complexes.
la source