3

動物オブジェクトのコレクションがあります。

私のコア コードでは、これらすべてを同じように動物として扱いたいと考えています。各動物は何らかの方法で処理する必要があります。処理の性質は、動物のサブタイプ (鳥、哺乳類など) によって異なります。

私のコードは現在次のようになっています。

public interface Animal {
    public String getTaxonomyClass(); 
}

public abstract class Bird implements Animal {

    @Override
    public String getTaxonomyClass() {
        return "aves";
    }

    // Specific to birds
    public abstract float getWingspan();

}

public abstract class Mammal implements Animal {

    @Override
    public String getTaxonomyClass() {
        return "mammalia";
    }

    // Specific to mammals
    public abstract int getToothCount();

}

public interface AnimalProcessor {
    public String getSupportedTaxonomyClass();
    public void process(Animal a);
}

public class MammalProcessor implements AnimalProcessor {

    @Override
    public String getSupportedTaxonomyClass() {
        return "mammalia";
    }

    @Override
    public void process(Animal a) {
        System.out.println("Tooth count is " + ((Mammal)a).getToothCount());
    }

}

public class BirdProcessor implements AnimalProcessor {

    @Override
    public String getSupportedTaxonomyClass() {
        return "aves";
    }

    @Override
    public void process(Animal a) {
        System.out.print("Wingspan is " + ((Bird)a).getWingspan());
    }

}

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZooKeeper {

    Map<String, AnimalProcessor> registry = new HashMap<String, AnimalProcessor>();

    public void registerProcessor(AnimalProcessor ap)
    {
        registry.put(ap.getSupportedTaxonomyClass(), ap);
    }

    public void processNewAnimals(List<Animal> newcomers)
    {
        for(Animal critter : newcomers)
        {
            String taxonomy = critter.getTaxonomyClass();
            if(registry.containsKey(taxonomy))
            {
                // if I can process the animal, I will
                AnimalProcessor ap = registry.get(taxonomy);
                ap.process(critter);
            }

        }
    }
}

import java.util.LinkedList;
import java.util.List;

public class MainClass {

    public static void main(String[] args) {

        ZooKeeper keeper = new ZooKeeper();
        keeper.registerProcessor(new MammalProcessor());
        keeper.registerProcessor(new BirdProcessor());

        List<Animal> animals = new LinkedList<Animal>();

        animals.add(new Mammal() {  // badger

            @Override
            public int getToothCount() {
                return 40;
            } } 
        );

        animals.add(new Bird() {  // condor

            @Override
            public float getWingspan() {
                return 2.9f;
            } }
        );

        keeper.processNewAnimals(animals);

    }
}

一般的に、これは理解しやすく、うまく機能します。ZooKeeper クラスやインターフェイスを変更することなく、プラグインの新しいプロセッサと動物の種類を自由に追加できます。データベースから動物をロードし、それらすべてを順番に処理する、より高度なメイン クラスを想像できます。

ただし、AnimalProcessor サブクラス内のダウンキャストが心配です! これはあってはならないことであり、オブジェクト指向の原則に違反している可能性があります。結局、現時点では鳥を MammalProcessor の process() メソッドに渡すことができ、ClassCastException が発生します。

これを解決するための設計パターンを提案できる人はいますか? Visitor パターンを見ましたが、この場合の適用方法がよくわかりませんでした! 重要なのは、コア コード (ZooKeeper) ですべての動物を同じように扱い、新しい動物のサポートを簡単に追加できるようにすることです。ありがとう!

4

6 に答える 6

3

次のことをお勧めします。

public interface Animal {
    public AnimalProcessor<? extends Animal> getProcessor();
}

そのため、各動物は一致するプロセッサを返します。

public interface AnimalProcessor<T extends Animal> {
     public void process(T a);
}

したがって、プロセッサは、処理する必要がある一致するタイプで型付けされます。したがって、注入は次のようになります。

public abstract class Bird implements Animal {
    private BirdProcessor processor = new BirdProcessor();
    public abstract float getWingspan();
    @Override
    public AnimalProcessor<Bird> getProcessor() {
        return processor; 
    }
}

public class BirdProcessor implements AnimalProcessor<Bird> {
    @Override
    public void process(Bird b) {
        System.out.print("Wingspan is " + b.getWingspan());
    }
}
于 2012-05-10T13:34:25.307 に答える
2

これはジェネリックがうまく機能するところです。

まず、AnimalProcessorを汎用にする必要があります。

public interface AnimalProcessor <T extends Animal> {
    public String getSupportedTaxonomyClass();
    public void process(T a);
}

次に、特定のプロセッサで、ジェネリック型を指定します。たとえば、哺乳類の場合:

public class MammalProcessor implements AnimalProcessor<Mammal> {

    public String getSupportedTaxonomyClass() {
        return "mammalia";
    }

    public void process(Mammal a) {
        System.out.println("Tooth count is " + a.getToothCount());
    }

}

現在、プロセスメソッドは哺乳類オブジェクトのみを受け入れ、ここでは鳥を受け入れません。

于 2012-05-10T13:51:11.670 に答える
1

あなたをAnimalProcessor一般的にしてください。

public interface AnimalProcessor<T extends Animal> {
    public String getSupportedTaxonomyClass();
    public void process(T a);
}

public class MammalProcessor implements AnimalProcessor<Mammal> {

    @Override
    public String getSupportedTaxonomyClass() {
        return "mammalia";
    }

    @Override
    public void process(Mammal a) {
        System.out.println("Tooth count is " + a.getToothCount());
    }

}
于 2012-05-10T13:53:11.920 に答える
1

次のことをお勧めします。

public interface Animal {
    public String getTaxonomyClass(); 
    public void process();
}

ここで、Animal を実装する各動物クラスは、独自の処理ロジックを実装する必要があります。例えば ​​:

public class Bird implements Animal {

    public Bird(float wingSpan) {
        this.wingSpan = wingSpan;
    }

    @Override
    public String getTaxonomyClass() {
        return "aves";
    }

    @Override
    public void process() {
         System.out.print("Wingspan is " + wingSpan);
    }

    // Specific to birds
    private float wingspan;
}

これで、次のように処理する AnimalProcessor を 1 つだけ持つことができます。

 public void process(Animal a) {
      a.process();
 }
于 2012-05-10T13:24:05.960 に答える
0

だから、あなたはこのようなクラスを持っています...

public abstract class Bird implements Animal {

    @Override
    public String getTaxonomyClass() {
        return "aves";
    }

    // Specific to birds
    public abstract float getWingspan();

}

翼幅が であっても、すべてBirdsに翼幅があり0ます。では、クラスを次のように変更してみませんか...

public class Bird implements Animal {

    float wingspan = 0.0f;

    public Bird(float wingspan){
        this.wingspan = wingspan;
    }

    @Override
    public String getTaxonomyClass() {
        return "aves";
    }

    // Specific to birds
    public float getWingspan(){
        return wingspan;
    }

}

Birdしたがって、これを行う代わりに、新しい を作成するには...

    animals.add(new Bird() {  // condor

        @Override
        public float getWingspan() {
            return 2.9f;
        } }
    );

あなたはただこれをするでしょう...

animals.add(new Bird(2.9f)); // condor

これにより、全体がよりシンプルになり、目的に合わせて使いやすくなるようです。Mammalクラスにも同様の変更を行います。

さて、動物の処理について...すべて処理する場合は、別のクラスを必要とせずに実装するAnimalsだけで済みます。これを行うには、 でメソッドを宣言します。あなたはこのように実装します...process()BirdBirdProcessorAnimalpublic void process();Bird

public void process() {
     System.out.print("Wingspan is " + getWingspan());
}

そして、単にこれを行うように変更しますAnimalProcessor(注:もはやインターフェースではありません)...

public class AnimalProcessor {
    public void process(Animal a) {
        a.process();
    }
}

その後AnimalProcessor、クラスはすべてを処理できるようになりますAnimals

または、そのままにAnimalProcessorしておく場合は、次のように変更することをお勧めしますClassCastException(このコードは のためのものBirdProcessorです)...

public void process(Animal a) {
    if (a instanceof Bird){
        System.out.print("Wingspan is " + ((Bird)a).getWingspan());
    }
}

これはあなたが探していたものですか?

于 2012-05-10T13:19:27.593 に答える
0

あなたの問題は次のような方法です

   public abstract int getToothCount();

...動物では定義されていません。代わりに、それらは Animal の特定のサブクラスで定義されます。つまり、動物は根本的に異なるため、動物を一般的に扱うことはできません。

これを克服するための 1 つの方法は、Animal クラスでこれらすべての抽象メソッドを作成することです。

Bird は getToothCount() に「0」で応答する場合があります。

すべての動物は getWingspan()、getTootCount() などに応答できるため、タイプ固有のチェックを実行する必要はありません。これで十分でない場合は、Animal で「boolean hasWings()」、「boolean hasTeeth()」などの抽象的な実装を作成します。

ここで、ある動物 a について次のように言うことができます。

if (a.hasWings()) System.out.println("My wingspan is "+a.getWingSpan());

どんな動物にも効きます。もちろん、Animal の各サブクラスは、さまざまなメソッドをすべて実装する必要があります。

もう 1 つのオプションは、非抽象メソッドを Animal に追加することです。これらのメソッドはデフォルトの回答を提供します。たとえば、getWingSpan() は 0 を返し、getToothCount() は 0 を返します。Shark は getToothCount() をオーバーライドします。Eagle は getWingSpan() をオーバーライドします...

そうすれば、サブクラスは、それらに直接関連するメソッドをオーバーライドする (または知っている) だけで済みます。

于 2012-05-10T13:26:48.393 に答える