3

我有一个 Animal 对象的集合。

我的核心代码希望将所有这些都视为动物,都一样。每个动物都需要以某种方式进行处理。处理的性质取决于动物的子类型(鸟类、哺乳动物等)。

我的代码目前如下所示。

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 类或任何接口。你可以想象一个更高级的主类,从数据库中加载 Animals,然后依次处理它们。

但是,我担心 AnimalProcessor 子类中的向下转换!这让我觉得不应该存在,并且可能违反了 OO 原则。毕竟,此刻我可以将 Bird 传递给 MammalProcessor 的 process() 方法,并且会出现 ClassCastException。

任何人都可以提出一个设计模式来解决这个问题吗?我查看了访问者模式,但不太清楚如何在这种情况下应用它!关键是让核心代码 (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());
    }

}

现在, process 方法只接受 Mammal 对象,这里不接受鸟类。

于 2012-05-10T13:51:11.670 回答
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 处理如下:

 public void process(Animal a) {
      a.process();
 }
于 2012-05-10T13:24:05.960 回答
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 回答
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()而不Bird需要单独的BirdProcessor类。为此,请在Animal中声明一个方法public 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 的特定子类中定义的。这意味着您不能笼统地对待动物,因为它们根本不同。

为了克服这个问题,一种方法是在 Animal 类中为所有这些创建抽象方法。

Bird 可能会以“0”响应 getToothCount()。

由于所有动物都可以响应 getWingspan()、getTootCount() 等,因此您不必执行任何特定于类型的检查。如果这还不够好,请在 Animal 中创建“boolean hasWings()”、“boolean hasTeeth()”等的抽象实现。

现在你可以说,对于一些动物:

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

这对任何动物都有效。当然,Animal 的每个子类都必须实现所有不同的方法。

另一种选择是向 Animal 添加非抽象方法。这些方法将提供默认答案。例如,getWingSpan() 将返回 0,getToothCount() 将返回 0,等等。Shark 将覆盖 getToothCount()。Eagle 将覆盖 getWingSpan()...

然后你的子类只需要覆盖(甚至知道)与它们直接相关的方法。

于 2012-05-10T13:26:48.393 回答