Preface
最近在研究一个框架JSqlParser时,发现该框架使用了一种设计模式——访问者(Visitor)模式。遂在网上找了一下该设计模式相关知识,学习整理。
参考:http://www.cnblogs.com/idior/archive/2005/08/18/217500.html
概述
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
第一次这句话时,着实费解。关于对访问者模式的理解,可以先见下面这个例子,而后再看最后的综述。
举个例子
比如:你下午请客喝东西,那么就的考虑好你请的这些人中总会有口味不同,有些人喜欢喝茶,有些人喜欢喝果汁,有人喜欢喝咖啡。你当然只有把这些东西都准备好,尽量准备得全一些。那么来的客人喜欢喝啥就自己拿就是了。
那么我们定义一个基本的访问者操作的接口,反正你也不晓得要来的客人喜欢喝什么,那么就抽象一个Visitor什么都喝。
public interface Water {
void accept(Visitor visitor);
}
此外,你得把具体得水准备好了,准备好三样:茶、咖啡、果汁。不同类型的水也许会有各自的一些属性操作等等。
public class CaffeeWater implements Water {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void addSugar(int number){
System.out.print("添加了"+number+"勺糖");
}
}
public class ZhangsanVisitor implements Visitor{
@Override
public void visit(TeaWater teaWater) {
System.out.print("张三只喝茶,");
String hotTea = teaWater.getHotTea();
System.out.println("拿了"+hotTea);
}
@Override
public void visit(JuiceWater juiceWater) {
//不喜欢果汁,什么操作也不做
}
@Override
public void visit(CaffeeWater caffeeWater) {
//不喜欢咖啡,
}
}
public class Table {
//准备好所有要喝的东西
List waters = new ArrayList<>();
public void addWater(Water water){
waters.add(water);
}
//招待客人
public void accept(Visitor visitor){
for (Water water : waters){
water.accept(visitor);
}
}
}
万事具备,客人来了,我们写个测试方法。准备好茶、咖啡、果汁来招待zhangsan、lisi。
@Test
public void test01(){
Table table = new Table();
table.addWater(new TeaWater());//摆上茶
table.addWater(new JuiceWater()); //摆上果汁
table.addWater(new CaffeeWater()); //摆上咖啡
Visitor zhangsan = new ZhangsanVisitor();
Visitor lisi = new LisiVisitor();
System.out.println("张三");
table.accept(zhangsan);
System.out.println("李四");
table.accept(lisi);
}
运行结果如下:
以上就是一个访问者模式的基本实例。
综述
关于访问者模式的UML图,可参见如下:
根据之前例子,可看到,对于访问者模式而言,两个最重要的角色:
- 访问者角色(Visitor):声明了一个或者多个访问操作,也就是说,该访问者需要做哪些操作;
- 节点角色(Element): 也就是Water这个接口的,它声明一个接受操作,接受一个访问者对象作为一个参量,也就是说,当访问者到来时,需要怎么做。
针对节点角色于访问者角色的方法可看到,节点角色接收一个访问者对象,并把自己作为变量再传给访问者对象,访问者对象再调用节点对象里的逻辑方法,这种操作过程被称为“双重分派”。
此外,对于Table这个对象,就是访问者模式的结构对象角色(ObjectStructure),它就是用于遍历节点中的所有元素。
可以看出,访问者模式适合于操作相对稳定的结构,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。也就是说,对于一个Visitor,它需要有什么操作,必须要非常清楚。如之前的例子,我招待客人,那么我很清楚,我只有茶、咖啡、果汁这个材料,我也只能拿这三个来招待客人。所以在Visitor方法中,就只有那三个方法。这是访问者模式最重要的一点,也是最有缺陷的一点。 如上个例子:有个人还想喝酒,我还得新增加一个节点类,及Water实现类,那么我还得在Visitor中加入一个方法,改动所有的Visitor的实现类,很明显,这样的效率是非常低下的。
但是对于增加新的访问者那就非常的简单,如上的例子,无非就是多来一个客人,也就是新增加一个新Visitor实现类。
所以,访问者适用于具体操作能够非常稳定的情景下,能够很容易的拓展访问者(Visitor)。
发表评论