10
votes

Éviter l'utilisation de «instance de»

Je me débats avec comment je pourrais éviter d'utiliser une instanceof () dans certains de mes codes. Cet exemple de réveillé quelque peu capture le problème.

Class Meat extends Food;

Class Plant extends Food;

Class Animal;

Class Herbivore extends Animal
{
    void eat( Plant food);
}

Class Carnivore extends Animal
{
    void eat( Meat food);
}

Class Omnivore extends Animal
{
    void eat(Food food);
}

Class Zoo
{
    List<Animals> animals;

    void receiveFood( Food food)
    {
        // only feed Plants to Herbivores and Meat to Carnivores
        // feed either to Omnivores
    }
}


8 commentaires

Peut-être devriez-vous examiner des méthodes génériques? De cette façon, vous pouvez spécifier qu'un animal a une méthode mangée (T étendre la nourriture) et l'herbivore serait dactylographié comme une plante? Quelque chose le long de ces lignes.


duplicailler possible de est cette utilisation de l'opérateur "Instanceof" Considéré comme mauvais design?


Candidat parfait pour le modèle de visiteur


Hmmm ... une solution de modèle de visiteur n'a-t-elle pas un problème similaire (1) ci-dessus? Lorsque la nourriture est reçue, un animal devrait être visité même s'il n'a pas mangé le type de nourriture? Un exemple de visiteur serait génial-- Je ne suis jamais sûr si je reçois complètement ce modèle dans certains contextes ...


Votre préoccupation concernant l'inefficacité n'est probablement pas très bien fondée. Chaque animal doit être nourri, le coût d'itération va donc être linéaire. Dans le pire des cas (où chaque animal est un herbavore), vous allez nourrir N mais demandant la permission 2 * n fois. Ceci est très probablement une inefficacité raisonnable. Si vous vous nourrissez très souvent et que les configurations d'animaux changent rarement, cela peut valoir la pré-traiter dans des listes d'herbavores, d'omnivores et de carnivores.


Quelle est l'interface de l'animal super type? Est-ce que cela traite avec ? Si tel est le cas, toute l'abstraction des animaux et de la nourriture ne serait-elle pas une violation du principe de substitution de Liskov? Peut-être que OOP n'est pas toujours la meilleure abstraction des taxonomies de la nature ...


Je dirais probablement que c'est un abus d'héritage. @nansen a raison, cela viole l'un des principes solides. La violation du LSP ne vous fera aucune fin du chagrin, malheureusement, vous ne vous en ressent souvent pas la majeure jusqu'à ce que vous soyez bien dans votre implémentation.


Cela semble très similaire à celui-ci Stackoverflow.com/Questtions/9806785/...


5 Réponses :


5
votes

Dans votre cas, si le consommateur de l'objet doit connaître certaines choses à propos de cet objet (par exemple, c'est de la viande de viande), incluez une propriété dans votre classe de base ISMeat () et avoir des sous-classes concrètes remplacent le Mise en œuvre de la méthode de la classe de base pour renvoyer une valeur appropriée.

Laisser cette connaissance dans la classe elle-même, plutôt que dans les consommateurs de la classe.


1 commentaires

Donc, le cas de mise en place d'une méthode ISMeat () dans la classe de base étant meilleur que d'utiliser l'instance est que dans le cas de cette dernière connaissance de la mise en œuvre (possible) est en cours de transfert à la classe client? Je suppose que cela viendrait ensuite à peser ce transfert de connaissances au client avec encombrement de l'objet de base avec iSxxx méthodes. Je peux voir que différents cas peuvent peut-être basculer la balance dans les deux sens.



4
votes

Une solution simple est lorsque vous utilisez plusieurs classes personnalisées interagissant les unes avec les autres, crée simplement ISNOOD (), Isanimal (), Iscarnivore (), etc. Méthodes qui renvoient un booléen en fonction de la classe. Ce n'est pas le Plus jolie mais le travail fait 100% du temps.


1 commentaires

Voir le commentaire ci-dessus. Vous avez donné fondamentalement la même réponse en même temps!



1
votes

Élargir sur mon commentaire, je tenterais d'utiliser des génériques pour m'aider ici ici: xxx

Notez que cela ne résout pas le problème d'itération via une liste de et animal et n'envoie que des aliments appropriés à chaque animal - je ne vois pas un moyen de le faire sans exemple explicite de chèques de style. Mais cela vous permet d'être plus précis avec ce que vos sous-classes acceptent.


1 commentaires

Voir Stackoverflow.com/questions/9806785/... Cette question a été mentionnée ci-dessus, mais il discute de certains des pièges potentiels d'une telle utilisation des génériques. Pour l'enregistrement, cependant, j'avais déjà envisagé d'une telle solution.



0
votes

Une autre solution consiste à maintenir 2 listes: une pour herbivores et une pour les carnivores.


2 commentaires

C'est une chose que j'ai pensée, mais il semble que j'avais encore besoin d'utiliser l'instanceOf () pour tester le type de nourriture pour décider de la liste des aliments.


Eh bien, vous auriez également besoin de canaliser des canaux pour obtenir de la nourriture: on ne fournit que des plantes que l'autre fournissant de la viande.



12
votes

Le motif de visiteur résout votre problème. Voici le code: xxx

exécutant le zoo impressions de démonstration xxx


2 commentaires

Code refactored en extrayant l'interface animalvistor de la nourriture de classe


Bien que le Nerd en moi aime voir le code pour le modèle de visiteur en action (Bravo!), Il a été conçu pour ajouter de nouveaux visiteurs sans casser les classes visitées, et non pour éviter l'instanceOf (qui est la question de la question). Les solutions de propriété (ISMeat () etc.) sont moins complexes et Humbler < / a>.