一、概述
访问者模式即Visitor模式,可以在不修改已有类的情况下,增加新的操作,使得分离对象的数据和行为,访问者模式也是行为型模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
概念看起来很难懂,举个具体的例子,说到访客,平时我们家里会有亲戚朋友过来串门,亲戚朋友就是访客,他们可以做一些事,也不能做全部的事,比如能吃饭,看电视,但是不能睡觉,洗碗;再比如我们去各种景点、博物馆游玩,那我们就是访客,我们可以游览、购物,但是不能搞破坏等等。
访问者模式主要由五个角色组成:
- 抽象访问者(Visitor):高度抽象的接口,其中声明了一个或者多个方法操作,是所有的具体访问者角色父接口。
- 具体访问者(ConcreteVisitor):实现抽象访问者的接口,在抽象的方法中实现自己具体的逻辑。
- 抽象节点(Node):声明一个接受操作,接受一个访问者对象作为一个参数。
- 具体节点(ConcreteNode):实现了抽象节点所规定的接受操作。
- 结构对象(ObjectStructure):可以遍历结构中的所有元素。
那么我们就以朋友串门来作为具体的例子说明。
二、使用
首先创建抽象的访问者Visitor,访问者可以吃饭、看电视这些具体的节点。
/**
* 抽象访问者,访问者可以在家里吃饭、看电视
*/
public interface Visitor {
void visit(Eat eat);
void visit(WatchTV watchTV);
}
再来看抽象节点。以家作为抽象节点的根据,里面有能做什么事的抽象方法。
/**
* 抽象的节点
*/
public interface Home {
void canDo(Visitor visitor);
}
然后就是具体的节点,表示能做的具体事情。
/**
* 吃饭
*/
public class Eat implements Home{
private static final String TAG = "XXX";
@Override
public void canDo(Visitor visitor) {
visitor.visit(this);
}
public void eat(String name){
Log.e(TAG, name + ",吃饭");
}
}
/**
* 看电视
*/
public class WatchTV implements Home{
private static final String TAG = "XXX";
@Override
public void canDo(Visitor visitor) {
visitor.visit(this);
}
public void watch(String name){
Log.e(TAG, name + ",看电视");
}
}
再然后就是具体的访问者角色了,这里创建了张三、李四这两个访问者。
/**
* 具体的访问者——张三
*/
public class ZhangSanVisitor implements Visitor {
private String name;
public ZhangSanVisitor(String name){
this.name = name;
}
@Override
public void visit(Eat eat) {
eat.eat(name);
}
@Override
public void visit(WatchTV watchTV) {
watchTV.watch(name);
}
}
/**
* 具体的访问者——李四
*/
public class LiSiVisitor implements Visitor{
private String name;
public LiSiVisitor(String name){
this.name = name;
}
@Override
public void visit(Eat eat) {
eat.eat(name);
}
@Override
public void visit(WatchTV watchTV) {
watchTV.watch(name);
}
}
最后就是结构对象,此类用来遍历结构中的所有元素。
/**
* 结构对象
*/
public class ObjectStructure {
private List<Home> homeList = new ArrayList<>();
public void addObj(Home home){
homeList.add(home);
}
public void doAction(Visitor visitor){
for(Home home : homeList){
home.canDo(visitor);
}
}
}
来测试一下,首先创建了结构对象,向其中添加了两个节点,即吃饭和看电视,接着创建一个具体的访问者——张三,在结构对象中遍历访问者能做哪些事情。打印输出如下如所示。
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addObj(new Eat());
objectStructure.addObj(new WatchTV());
Visitor visitor = new ZhangSanVisitor("张三");
objectStructure.doAction(visitor);
Visitor visitor1 = new LiSiVisitor("李四");
objectStructure.doAction(visitor1);

三、总结
从上面的代码中可以看到,访问者模式的扩展性很好,可以在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能,如果有新的访问者——王五,直接实现访问者接口既即可;符合单一职责原则,通过访问者将无关的行为分离,使职责单一。
访问者模式也有一些缺点:违反了迪米特原则,因为具体元素对访问者公布细节;也违反了依赖倒置原则,依赖了具体类,没有依赖抽象,可以看到在访问者接口中引用了具体的节点;对象结构变化困难,若对象结构发生了改变,访问者的接口和访问者的实现也都要发生相应的改变;
github地址:github.com/leewell5717…