以下是我用更好的代码示例提出的问题的后续问题:
以下代码使用访问者模式:
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())
答案 0 :(得分:6)
我认为为抽象类(在你的情况下为Animal)实现访问方法没有意义。
在访客中,您始终知道所有可能的子类型。它是一种基本假设(否则你向Visitor接口添加新方法)。但您获得了动态实现不同行为的能力。 在你的情况下它说话和走路。
支付的价格是实施"接受"每种具体类型的方法。 您试图提供更通用的解决方案并感到困惑:) 例如,请查看Wikipedia说明。
他们正在谈论Car的不同部分,但想法是一样的:他们为所有部分实施接受方法。
希望这有帮助
答案 1 :(得分:0)
如前面的回答所述,accept
方法必须是抽象的,并在所有具体的子类型中实现。
现在,它们是您实施的另外两个问题:
Dog
类不是抽象的。这意味着如果您在Dog
中提供了accept方法的默认实现,则会失去使用访问者模式的主要原因:您无法确定是否正确处理了Dog的所有子类型。Visitor
种方法“返回”void
:这会强制所有Visitor
实例通过副作用运行。您可能会发现corrected visitor pattern更不容易出错。然后,访问者模式非常冗长,因此如果您想真正了解维护工作量,请考虑使用代码生成器(注释处理器)。我可以推荐其中两个: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);
}
}