我正在努力避免在我的一些代码中避免使用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()
来测试食物的类型。
instanceOf()
来测试食物的类型,看看哪种食物可以喂食它们。 。至少这会更有效率,因为我不会给那些不吃它的动物喂食。
我已经想到了其他一些方法,但同样,他们似乎只是通过instanceof()
降压。
有什么建议吗?或者这(2,至少)是否可以接受instanceof()
使用?
答案 0 :(得分: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());
}
}
运行Zoo
演示打印
Herbivore eating Plant...
Omnivore eating Plant...
Carnivore eating Meat...
Omnivore eating Meat...
答案 1 :(得分:5)
在您的情况下,如果对象的使用者必须知道关于该对象的某些事物(例如它是肉),请在基类isMeat()
中包含一个属性,并使具体的子类覆盖基类的实现返回适当值的方法。
将这些知识留在班级本身,而不是在班级的消费者中。
答案 2 :(得分:4)
一个简单的解决方案是当使用多个自定义类相互交互时,只需创建isFood(),isAnimal(),isCarnivore()等方法,这些方法根据它们所在的类返回一个布尔值。它不是最漂亮,但它可以100%的时间完成工作。
答案 3 :(得分:1)
扩展我的评论,我会尝试使用泛型来帮助我:
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(); }
}
请注意,这仍然无法解决迭代Food
和Animal
列表并且只向每只动物发送适当食物的问题 - 我看不到这样做的方法没有明确的instanceof
样式检查。但是,它确实允许您更具体地了解子类接受的内容。
答案 4 :(得分:0)
另一个解决方案是维护2个列表:一个用于草食动物,一个用于食肉动物。