Java访客模式

时间:2016-02-05 09:22:28

标签: java visitor-pattern

我尝试使用扩展类的访问者模式。我有动物类列表,每个动物都是不同的。当我给访问者打电话时,它只执行谈话(动物a),而不是对象的具体实例。见下文:

class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}

class Visitor {
    public void talk(Animal a) { System.out.println("?"); }
    public void talk(Cat a) { System.out.println("Meow"); }
    public void talk(Dog a) { System.out.println("bark"); }
    public void talk(Poodle a) { System.out.println("Arf"); }
}    

public class Demo{

    public static void main(String []args){
        Visitor visitor = new Visitor();
        Animal list[] = { new Cat(), new Dog(), new Poodle() };

        for (Animal a : list)
            visitor.talk(a);
    }
}

输出是:

?
?
?

我期待:

Meow
bark
Arf 

知道如何在不将多个instanceof放入单个talk()方法的情况下实现访问者吗?

2 个答案:

答案 0 :(得分:6)

Visitor pattern的关键元素是double-dispatch:您需要从实际的子类调用访问者方法

abstract class Animal {
  abstract void accept(Visitor v);
}

class Dog extends Animal {
  @Override void accept(Visitor v) { v.talk(this); }
}

// ... etc for other animals.

然后,您将访客传递给动物,而不是动物传递给访客:

for (Animal a : list)
  a.accept(visitor);

原因是编译器选择Visitor上的重载来调用 - 它在运行时未被选中。所有编译器都知道aAnimal,所以它知道它唯一可以安全调用的方法是Visitor.visit(Animal)重载。

答案 1 :(得分:0)

访问者模式基本上是一个多对多的类行为解析机制。为了对您的动物实例有用/适用,我们需要添加访问者,正如Andy Turner所说。

如何包括"动物训练师"作为访客?不同的动物训练师可以让不同的动物以不同的方式说话(或不是)。

所以,首先,实体(动物界面)。我喜欢将此视为" Thing"被演员"

采取行动
public interface IAnimal {

    public String getAnimalName();

    // aka...  public void accept(Visitor v)
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer);
}

然后让我们定义一个访客类型(动物训练师界面)。我喜欢把访客想象成一个"演员"采取行动的各种事情" (实体):

public interface IAnimalTrainer {

    public String getTrainerName();

    //aka... public void visit(Dog dog);
    public void animalTrainerMakesAnimalSpeak(Dog dog);
    public void animalTrainerMakesAnimalSpeak(Cat cat);
    public void animalTrainerMakesAnimalSpeak(Poodle poodle);
}

现在让我们创建动物(实现IAnimal接口):

猫:

public class Cat implements IAnimal {

    private String animalName;

    @Override
    public String getAnimalName() {
        return animalName;
    }
    public void setAnimalName(String animalName) {
        this.animalName = animalName;
    }

    public Cat(String animalName) {
        this.animalName = animalName;
    }
    public Cat() {
        // Default constructor
    }

    @Override
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) {
        animalTrainer.animalTrainerMakesAnimalSpeak(this);
    }
}

犬:

public class Dog implements IAnimal {

    private String animalName;

    @Override
    public String getAnimalName() {
        return animalName;
    }
    public void setAnimalName(String animalName) {
        this.animalName = animalName;
    }

    public Dog(String animalName) {
        this.animalName = animalName;
    }
    public Dog() {
        // Default constructor
    }

    @Override
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) {
        animalTrainer.animalTrainerMakesAnimalSpeak(this);
    }
}

贵宾犬:

public class Poodle extends Dog {

    public Poodle(String animalName) {
        super(animalName);
    }
    public Poodle() {
        super();
    }

    @Override
    public void allowAnimalTrainerToMakeMeSpeak(IAnimalTrainer animalTrainer) {
        animalTrainer.animalTrainerMakesAnimalSpeak(this);
    }
}

现在让我们创建动物训练师Phil和Jack(实现IAnimalTrainer接口):

动物训练师菲尔:

public class AnimalTrainerPhil implements IAnimalTrainer {

    private String trainerName = "Phil";

    @Override
    public String getTrainerName() {
        return trainerName;
    }
    public void setTrainerName(String trainerName) {
        this.trainerName = trainerName;
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Dog dog) {
        System.out.println(
                "Animal trainer " 
                + getTrainerName()
                + " gets " 
                + dog.getAnimalName() 
                + " the dog to say: BARK!!");
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Cat cat) {
        System.out.println(
                "Animal trainer "
                + getTrainerName()
                + " gets " 
                + cat.getAnimalName() 
                + " the cat to say: MEOW!!");
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Poodle poodle) {
        animalTrainerMakesAnimalSpeak((Dog)poodle);
    }
}

动物训练师杰克:

public class AnimalTrainerJack implements IAnimalTrainer {

    private String trainerName = "Jack";

    @Override
    public String getTrainerName() {
        return trainerName;
    }
    public void setTrainerName(String trainerName) {
        this.trainerName = trainerName;
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Dog dog) {
        System.out.println(
                "Animal trainer " 
                + getTrainerName()
                + " gets " 
                + dog.getAnimalName() 
                + " the dog to say: Bark bark.");
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Cat cat) {
        System.out.println(
                "Animal trainer " 
                + getTrainerName()
                + " gets " 
                + cat.getAnimalName() 
                + " the cat to say: Meoooow.");
    }

    @Override
    public void animalTrainerMakesAnimalSpeak(Poodle poodle) {
        System.out.println(
                "Animal trainer " 
                + getTrainerName()
                + " gets " 
                + poodle.getAnimalName() 
                + " the poodle to say: Yip! Yip!");
    }
}

现在让我们把这一切都拉到一起,让所有动物训练师(菲尔和杰克)通过经理课程让所有的动物(猫,狗,贵宾犬)说话:

public class ManagerOfAnimalTrainersAndAnimals {

    public static void main(String[] args) {

        ArrayList<IAnimal> allAnimals = new ArrayList<>();
        allAnimals.add(new Dog("Henry"));
        allAnimals.add(new Cat("Priscilla"));
        allAnimals.add(new Poodle("Spike"));

        ArrayList<IAnimalTrainer> allAnimalTrainers = new ArrayList<>();
        allAnimalTrainers.add(new AnimalTrainerPhil());
        allAnimalTrainers.add(new AnimalTrainerJack());

        // Allow all animal trainers to get each animal to speak
        for (IAnimalTrainer animalTrainer : allAnimalTrainers) {
            for (IAnimal animal : allAnimals) {
                animal.allowAnimalTrainerToMakeMeSpeak(animalTrainer);
            }
        }
    }
}

这是输出:

Animal trainer Phil gets Henry the dog to say: BARK!! 
Animal trainer Phil gets Priscilla the cat to say: MEOW!! 
Animal trainer Phil gets Spike the dog to say: BARK!! 
Animal trainer Jack gets Henry the dog to say: Bark bark. 
Animal trainer Jack gets Priscilla the cat to say: Meoooow. 
Animal trainer Jack gets Spike the poodle to say: Yip! Yip!

这样可以相对轻松地添加能够访问动物类特定事物的新训练师(例如,如果您向猫添加爪信息,并向爪子添加爪子信息......),因为它们会对各种动物起作用。我认为传统&#34;访客&#34;例如,它太模糊,不够具体。出租车的例子对我来说也没办法。我希望这个例子有所帮助。