10

一部のコードで 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) eat( Food food)Animal に実装することができ、各サブクラスは食べない食べ物を無視することを選択できますが、それは非効率的であり、各 Animal サブクラスinstanceof()が食べ物の種類をテストするために使用する必要があります。

(2) 動物が食べる餌の種類に基づいて、3 つの動物のコレクションを動物園に保管できますが、instanceOf()どのコレクションに餌を与えるかを確認するために、餌の種類をテストするために使用する必要があります。少なくとも、食べない動物に食べ物を与えないので、これはより効率的です。

私はいくつかの他のアプローチを考えましたが、繰り返しになりますが、それらは単に責任をinstanceof()負っているようです.

助言がありますか?それとも、これ (少なくとも 2 つ) の許容可能な使用法instanceof()でしょうか?

4

5 に答える 5

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...
于 2012-04-04T20:14:45.900 に答える
5

あなたの場合、オブジェクトの消費者がそのオブジェクトに関する特定のことを知る必要がある場合(たとえば、肉など)、基本クラスにプロパティを含め、isMeat()具体的なサブクラスが基本クラスメソッドの実装をオーバーライドして適切な値を返すようにします。

クラスの消費者ではなく、クラス自体にその知識を残してください。

于 2012-04-04T19:13:24.783 に答える
4

簡単な解決策は、互いに相互作用する複数のカスタム クラスを使用する場合です。isFood()、isAnimal()、isCarnivore() などのメソッドを作成し、それらが属するクラスに応じてブール値を返します。 100% の確率で仕事を完了します。

于 2012-04-04T19:14:11.677 に答える
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ただし、サブクラスが受け入れるものをより具体的にすることができます。

于 2012-04-04T19:24:48.943 に答える
0

もう 1 つの解決策は、草食動物用と肉食動物用の 2 つのリストを維持することです。

于 2012-04-04T20:01:45.970 に答える