我只是想了解使用访客模式的主要好处。
这是一个Java实现示例
///////////////////////////////////
// Interfaces
interface MamalVisitor {
void visit(Mammal mammal);
}
interface MammalVisitable {
public void accept(MamalVisitor visitor);
}
interface Mammal extends MammalVisitable {
public int getLegsNumber();
}
///////////////////////////////////
///////////////////////////////////
// Model
class Human implements Mammal {
@Override
public void accept(MamalVisitor visitor) { visitor.visit(this); }
@Override
public int getLegsNumber() { return 2; }
}
//PIRATE HAS A WOOD LEG
class Pirate extends Human {
@Override
public int getLegsNumber() { return 1; }
public int getWoodLegNumber() { return 1; }
}
class Dog implements Mammal {
@Override
public void accept(MamalVisitor visitor) { visitor.visit(this); }
@Override
public int getLegsNumber() { return 4; }
}
///////////////////////////////////
///////////////////////////////////
class LegCounterVisitor implements MamalVisitor {
private int legNumber = 0;
@Override
public void visit(Mammal mammal) { legNumber += mammal.getLegsNumber(); }
public int getLegNumber() { return legNumber; }
}
class WoodLegCounterVisitor implements MamalVisitor {
private int woodLegNumber = 0;
@Override
public void visit(Mammal mammal) {
// perhaps bad but i'm lazy
if ( mammal instanceof Pirate ) {
woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
}
}
public int getWoodLegNumber() { return woodLegNumber; }
}
///////////////////////////////////
///////////////////////////////////
public class Main {
public static void main(String[] args) {
// Create a list with 9 mammal legs and 3 pirate woodlegs
List<Mammal> mammalList = Arrays.asList(
new Pirate(),
new Dog(),
new Human(),
new Pirate(),
new Pirate()
);
///////////////////////////////////
// The visitor method
LegCounterVisitor legCounterVisitor = new LegCounterVisitor();
WoodLegCounterVisitor woodLegCounterVisitor = new WoodLegCounterVisitor();
for ( Mammal mammal : mammalList ) {
mammal.accept(legCounterVisitor);
mammal.accept(woodLegCounterVisitor);
// why not also using:
// legCounterVisitor.visit(mammal);
// woodLegCounterVisitor.visit(mammal);
}
System.out.println("Number of legs:" + legCounterVisitor.getLegNumber());
System.out.println("Number of wood legs:" + woodLegCounterVisitor.getWoodLegNumber());
///////////////////////////////////
// The standart method
int legNumber = 0;
int woodLegNumber = 0;
for ( Mammal mammal : mammalList ) {
legNumber += mammal.getLegsNumber();
// perhaps bad but i'm lazy
if ( mammal instanceof Pirate ) {
woodLegNumber += ((Pirate) mammal).getWoodLegNumber();
}
}
System.out.println("Number of legs:" + legNumber);
System.out.println("Number of wood legs:" + woodLegNumber);
}
}
///////////////////////////////////
我只是想知道这种情况下使用这种模式的主要优势是什么。我们也可以遍历集合并获得几乎相同的东西,除了我们不必处理新的接口并将模板代码添加到模型中...
使用Apache Commons或功能语言,经典的方式似乎是做一些map / reduce操作(映射到腿数并减少添加),这很容易......
我也想知道为什么我们使用
mammal.accept(legCounterVisitor);
mammal.accept(woodLegCounterVisitor);
而不是
legCounterVisitor.visit(mammal);
woodLegCounterVisitor.visit(mammal);
第二个选项似乎删除了模型部件上的accept(...)方法。
在我发现的许多样本中,似乎他们没有为模型对象使用通用接口。我添加它是因为我只需要添加一个访问(哺乳动物)方法,而不是为每个哺乳动物实现一个。 让我的所有对象实现哺乳动物是好的吗? (我猜有时它无论如何都不可能)。它仍然是这样的访客模式吗?
所以我的问题是: - 你看到我使用访客的例子有什么好处吗? - 如果没有,你能为游客提供一些具体的用具吗? - 访问者在函数式编程语言中很有用
我发现与此模式相关的唯一例子是漂亮的打印机,在访问者的状态中保持在访问不同节点期间使用的偏移量(用于显示XML树的例子)
答案 0 :(得分:5)
访客模式只是double dispatch。
我不确定我是否同意您对访问者的实施。我会实现这样的东西:
interface MammalVisitor {
void visit(Pirate pirate);
void visit(Human human);
void visit(Dog dog);
}
// Basic visitor provides no-op behaviour for everything.
abstract class MammalAdapter implements MammalVisitor {
void visit(Pirate pirate) {};
void visit(Human human) {};
void visit(Dog dog) {};
}
然后实施变得更加清洁:
// We only want to provide specific behaviour for pirates
class WoodLegCounterVisitor extends MammalAdaptor {
private int woodLegNumber = 0;
@Override
public void visit(Pirate pirate) {
woodLegNumber += pirate.getWoodLegNumber();
}
public int getWoodLegNumber() { return woodLegNumber; }
}
在回答您的实际问题时,使用访问者的主要优点是无需进行“instanceof”检查。它使您能够将用于处理层次结构的逻辑分离到单独的类中。它还使您能够在不更改原始类的情况下添加新行为。
答案 1 :(得分:4)
Visitor pattern是一个花哨的开关案例/模式匹配系统,以方便graph traversal。
作为典型的功能语言提供模式匹配和有效的遍历图表的方式,兴趣更加有限。
即使在使用instanceof
或使用enum
的JAVA中,访问者更像是一种通用解决方案而不是通用解决方案,因为很多算法都不适合它。
答案 2 :(得分:3)
Visitor Pattern的目的是将对象结构(在您的情况下,Mammal
)与算法(在您的情况下,计数器腿计数器算法)分开。
整个想法是你的对象(主要是java,JavaBeans)根本不改变它的结构,只引入一个新的virtual function来引入一个新的算法。
与Jeff Foster的实现不同,可以使用泛型来简化代码。这为您的访问者带来了特殊性,例如:
public interface MammalVisitor<T extends Mammal> {
public void visit(T mammal);
}
public class LegCounterVisitor implements MamalVisitor<Human> {
private int legNumber = 0;
@Override
public void visit(Human mammal) { legNumber += mammal.getLegsNumber(); }
public int getLegNumber() { return legNumber; }
}
public class WoodLegCounterVisitor implements MamalVisitor<Pirate> {
private int legNumber = 0;
@Override
public void visit(Pirate mammal) {legNumber += mammal.getWoodLegNumber(); }
public int getLegNumber() { return legNumber; }
}