简体   繁体   English

避免使用“instanceof”

[英]Avoiding use of 'instanceof'

I'm struggling with how I might avoid using instanceof() in some of my code.我正在努力解决如何避免在我的某些代码中使用 instanceof() 的问题。 This contrived example somewhat captures the problem.这个人为的例子在某种程度上抓住了问题。

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
    }
}

Herbivores are only interested in Plants, Carnivores only in Meat and Omnivores both.食草动物只对植物感兴趣,食肉动物只对肉类感兴趣,杂食动物两者都感兴趣。 When the Zoo receives Food it only makes sense to try to feed food to animals that eat that type of food.当动物园收到食物时,只有尝试将食物喂给吃这种食物的动物才有意义。

I've thought of a few solutions, but all seem to depend on the use of instanceof() somewhere and my various refactorings just seem to move it around.我想到了一些解决方案,但似乎都依赖于在某处使用instanceof() ,而我的各种重构似乎只是将它四处移动。

(1) I could implement eat( Food food) in Animal and each subclass could choose to ignore food that it doesn't eat, but that is inefficient and would require that each Animal subclass use instanceof() to test the type of food. (1) 我可以在 Animal 中实现eat( Food food)并且每个子类都可以选择忽略它不吃的食物,但这是低效的并且需要每个 Animal 子类使用instanceof()来测试食物的类型。

(2) I could keep three collections of animals in the Zoo based on the type of food they eat, but would still have to use instanceOf() to test the type of food to see which collection to feed it to. (2) 我可以根据动物吃的食物类型在动物园中饲养三只 collections 动物,但仍然必须使用instanceOf()来测试食物类型以查看将其喂给哪个集合。 At least this would be more efficient as I wouldn't be feeding food to Animals that won't eat it.至少这会更有效率,因为我不会将食物喂给不会吃它的动物。

I've thought of some other approaches, but again, they just seem to pass the instanceof() buck.我想到了其他一些方法,但同样,它们似乎只是通过了instanceof()降压。

Any suggestions?有什么建议么? Or would this (2, at least) be an acceptable use of instanceof() ?或者这(至少 2 个)是可以接受的instanceof()用法吗?

The visitor pattern solves your problem.访问者模式解决了你的问题。 Here's the code:这是代码:

public abstract class Animal {
  public abstract void accept(AnimalVisitor visitor);
}

public interface AnimalVisitor {
  public void visit(Omnivore omnivore);
  public void visit(Herbivore herbivore);
  public void visit(Carnivore carnivore);
}

public class Carnivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Meat meat) {
    System.out.println("Carnivore eating Meat...");
  }
}

public class Herbivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Plant plant) {
    System.out.println("Herbivore eating Plant...");
  }
}

public class Omnivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Food food) {
    System.out.println("Omnivore eating " + food.getClass().getSimpleName() + "...");
  }
}

public abstract class Food implements AnimalVisitor {
  public void visit(Omnivore omnivore) {
    omnivore.eat(this);
  }
}

public class Meat extends Food {
  @Override
  public void visit(Carnivore carnivore) {
    carnivore.eat(this);
  }

   @Override
  public void visit(Herbivore herbivore) {
    // do nothing
  }
}

public class Plant extends Food {
   @Override
  public void visit(Carnivore carnivore) {
    // do nothing
  }

   @Override
  public void visit(Herbivore herbivore) {
    herbivore.eat(this);
  }
}

public class Zoo {
  private List<Animal> animals = new ArrayList<Animal>();

  public void addAnimal(Animal animal) {
    animals.add(animal);
  }

  public void receiveFood(Food food) {
    for (Animal animal : animals) {
      animal.accept(food);
    }
  }

  public static void main(String[] args) {
    Zoo zoo = new Zoo();
    zoo.addAnimal(new Herbivore());
    zoo.addAnimal(new Carnivore());
    zoo.addAnimal(new Omnivore());

    zoo.receiveFood(new Plant());
    zoo.receiveFood(new Meat());
  }
}

Running the Zoo demo prints运行Zoo演示打印

Herbivore eating Plant...
Omnivore eating Plant...
Carnivore eating Meat...
Omnivore eating Meat...

In your case, if the consumer of the object must know certain things about that object (eg is it meat), include a property in your base class isMeat() and have concrete subclasses override the implementation of the base class method to return an appropriate value.在你的情况下,如果 object 的消费者必须知道关于 object 的某些事情(例如它是肉),请在你的基础 class isMeat()中包含一个属性,并让具体的子类覆盖基础 class 方法的实现以返回适当的价值。

Leave that knowledge in the class itself, rather than in consumers of the class.将该知识留在 class 本身,而不是 class 的消费者中。

A simple solution is when using multiple custom classes interacting with one another, just create isFood(), isAnimal(), isCarnivore(), etc. methods that return a boolean depending on which class they're in. It's not the prettiest but it gets the job done 100% of the time.一个简单的解决方案是当使用多个自定义类相互交互时,只需创建 isFood()、isAnimal()、isCarnivore() 等返回 boolean 的方法,具体取决于它们所在的 class。这不是最漂亮的,但它100% 的时间完成工作。

Expanding on my comment, I would attempt to use generics to help me here:扩展我的评论,我会尝试使用 generics 来帮助我:

interface Animal<T extends Food> {
    void eat(T food);
}

class Herbivore extends Animal<Plant> {
    void eat(Plant food) { foo(); }
}

class Carnivore extends Animal<Meat> {
    void eat(Meat food) { bar(); }
}

Note that this still doesn't solve the problem of iterating through a list of Food and Animal and only sending appropriate food to each animal -- I don't see a way to do that without explicit instanceof style checks.请注意,这仍然不能解决遍历FoodAnimal列表并只向每只动物发送适当食物的问题——如果没有明确的instanceof样式检查,我看不出有什么方法可以做到这一点。 But, it does allow you to be more specific with what your subclasses accept.但是,它确实允许您更具体地了解您的子类接受的内容。

Another solution is to maintain 2 lists: one for Herbivores and one for Carnivores.另一种解决方案是维护 2 个列表:一个用于食草动物,一个用于食肉动物。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM