避免使用'instanceof'

Hol*_*osa 10 java oop polymorphism refactoring design-patterns

我正在努力避免在我的一些代码中避免使用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
    }
}
Run Code Online (Sandbox Code Playgroud)

草食动物只对肉类和杂食动物中的植物,食肉动物感兴趣.当动物园收到食物时,尝试向食用该类食物的动物喂食物是有意义的.

我已经想到了一些解决方案,但似乎都依赖于instanceof()某个地方的使用而我的各种重构似乎只是移动它.

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

(2)我可以根据他们吃的食物类型在动物园饲养三种动物,但是仍然需要instanceOf()用来测试食物的类型,看看哪种食物可以喂食.至少这会更有效率,因为我不会给那些不吃它的动物喂食物.

我已经想到了其他一些方法,但同样,他们似乎也instanceof()不屑一顾.

有什么建议?或者这(2,至少)是否可以接受instanceof()

chr*_*ris 12

访客模式解决了您的问题.这是代码:

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

运行Zoo演示打印

Herbivore eating Plant...
Omnivore eating Plant...
Carnivore eating Meat...
Omnivore eating Meat...
Run Code Online (Sandbox Code Playgroud)

  • 虽然我的书呆子喜欢看到访问者模式的代码(bravo!),但它是为了在不破坏Visited类的情况下添加新的Visitors而设计的,而不是为了避免instanceof(这就是问题所在).属性解决方案(isMeat()等)不那么复杂和[不起眼](http://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340.html). (3认同)

Eri*_* J. 5

在您的情况下,如果对象的使用者必须知道关于该对象的某些事物(例如它是肉),则在您的基类中包含一个属性,isMeat()并让具体的子类覆盖基类方法的实现以返回适当的值.

将这些知识留在课堂本身,而不是课堂上的消费者.