Java访客模式2

时间:2016-02-06 05:55:53

标签: java design-patterns visitor

以下是我用更好的代码示例提出的问题的后续问题:

以下代码使用访问者模式:

class Animal { void accept(Visitor v) { v.visit(this); } }
class Cat extends Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}

interface Visitor {
    public void visit(Animal a);
    public void visit(Cat a);
    public void visit(Dog a);
    public void visit(Poodle a);
}

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

class WalkVisitor implements Visitor {
    public void visit(Animal a) { System.out.println("?"); }
    public void visit(Cat a) { System.out.println("Sneak"); }
    public void visit(Dog a) { System.out.println("Walk"); }
    public void visit(Poodle a) { System.out.println("Skip"); }
} 

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

        for (Animal a : list)
            a.accept(new TalkVisitor());

        for (Animal a : list)
            a.accept(new WalkVisitor());    
    }
} 

输出结果为:

?
?
?
?

如何在不添加Animal.accept()中的instanceof开关的情况下修复它? (每次添加新的动物类时,我都不想保持switch())

2 个答案:

答案 0 :(得分:6)

我认为为抽象类(在你的情况下为Animal)实现访问方法没有意义。

在访客中,您始终知道所有可能的子类型。它是一种基本假设(否则你向Visitor接口添加新方法)。但您获得了动态实现不同行为的能力。 在你的情况下它说话和走路。

支付的价格是实施"接受"每种具体类型的方法。 您试图提供更通用的解决方案并感到困惑:) 例如,请查看Wikipedia说明。

他们正在谈论Car的不同部分,但想法是一样的:他们为所有部分实施接受方法。

希望这有帮助

答案 1 :(得分:0)

如前面的回答所述,accept方法必须是抽象的,并在所有具体的子类型中实现。

现在,它们是您实施的另外两个问题:

  1. Dog类不是抽象的。这意味着如果您在Dog中提供了accept方法的默认实现,则会失去使用访问者模式的主要原因:您无法确定是否正确处理了Dog的所有子类型。
  2. Visitor种方法“返回”void:这会强制所有Visitor实例通过副作用运行。您可能会发现corrected visitor pattern更不容易出错。
  3. 然后,访问者模式非常冗长,因此如果您想真正了解维护工作量,请考虑使用代码生成器(注释处理器)。我可以推荐其中两个:adt4j(jdk7 +)和derive4j(jdk8 +)。 这些生成器生成正确的子类和accept方法的实现。 对于你的例子,使用derive4j并应用上面的两个“修复”,你会写:

    import static Animals.*;
    import static Dogs.*;
    
    import java.util.Arrays;
    import java.util.function.Function;
    
    class Cat {}
    
    @org.derive4j.Data
    abstract class Dog {
        interface Visitor<R> {
            R Terrier(String name);
            R Poodle(String name);
        }
        abstract <R> R accept(Visitor<R> visitor);
    }
    
    @org.derive4j.Data
    abstract class Animal {
        interface Visitor<R> {
            R cat(Cat cat);
            R dog(Dog dog);
        }
        abstract <R> R accept(Visitor<R> visitor);
    
        public static void main(String[] args) {
    
            final Function<Animal, String> talkVisitor = Animals.cases()
                    .cat("Meow")
                    .dog(Dogs.cases()
                            .Poodle("Arf")
                            .otherwise("bark"));
    
            final Function<Animal, String> walkVisitor = Animals.cases()
                    .cat("Sneak")
                    .dog(Dogs.cases()
                            .Poodle("Skip")
                            .otherwise("Walk"));
    
            Animal[] list = { cat(new Cat()), dog(Terrier("Max")), dog(Poodle("Minus")) };
    
            for (Animal a : list)
                System.out.println(talkVisitor.apply(a));
    
            for (Animal a : list)
                System.out.println(walkVisitor.apply(a));
    
            // or better:
            Arrays.asList(list).stream()
                    .map(talkVisitor)
                    .forEach(System.out::println);
    
        }
    }