繁体   English   中英

避免使用“instanceof”

[英]Avoiding use of 'instanceof'

我正在努力解决如何避免在我的某些代码中使用 instanceof() 的问题。 这个人为的例子在某种程度上抓住了问题。

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

食草动物只对植物感兴趣,食肉动物只对肉类感兴趣,杂食动物两者都感兴趣。 当动物园收到食物时,只有尝试将食物喂给吃这种食物的动物才有意义。

我想到了一些解决方案,但似乎都依赖于在某处使用instanceof() ,而我的各种重构似乎只是将它四处移动。

(1) 我可以在 Animal 中实现eat( Food food)并且每个子类都可以选择忽略它不吃的食物,但这是低效的并且需要每个 Animal 子类使用instanceof()来测试食物的类型。

(2) 我可以根据动物吃的食物类型在动物园中饲养三只 collections 动物,但仍然必须使用instanceOf()来测试食物类型以查看将其喂给哪个集合。 至少这会更有效率,因为我不会将食物喂给不会吃它的动物。

我想到了其他一些方法,但同样,它们似乎只是通过了instanceof()降压。

有什么建议么? 或者这(至少 2 个)是可以接受的instanceof()用法吗?

访问者模式解决了你的问题。 这是代码:

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());
  }
}

运行Zoo演示打印

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

在你的情况下,如果 object 的消费者必须知道关于 object 的某些事情(例如它是肉),请在你的基础 class isMeat()中包含一个属性,并让具体的子类覆盖基础 class 方法的实现以返回适当的价值。

将该知识留在 class 本身,而不是 class 的消费者中。

一个简单的解决方案是当使用多个自定义类相互交互时,只需创建 isFood()、isAnimal()、isCarnivore() 等返回 boolean 的方法,具体取决于它们所在的 class。这不是最漂亮的,但它100% 的时间完成工作。

扩展我的评论,我会尝试使用 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(); }
}

请注意,这仍然不能解决遍历FoodAnimal列表并只向每只动物发送适当食物的问题——如果没有明确的instanceof样式检查,我看不出有什么方法可以做到这一点。 但是,它确实允许您更具体地了解您的子类接受的内容。

另一种解决方案是维护 2 个列表:一个用于食草动物,一个用于食肉动物。

暂无
暂无

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

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