访问者模式
1/26/24About 5 min
访问者模式提供了一种方便的、可维护的方法来表示在对象结构元素上要进行的操作。该模式允许不改变操作元素的类的前提下定义一个新操作。

访问者模式的主要角色:
- 抽象访问者(Visitor):它声明了一个访问操作的接口,该接口的实现可以访问一个由元素对象结构中的元素所组成的对象结构。
- 具体访问者(ConcreteVisitor):它实现了抽象访问者接口中声明的访问操作,以便为一个对象结构中的元素执行操作。
- 抽象元素(Element):它声明了一个接受操作的接口,该接口的实现可以为一个对象结构中的元素接受访问。
- 具体元素(ConcreteElement):它实现了接受操作接口,以便为一个对象结构中的元素接受访问。
- 对象结构(ObjectStructure):它可以是元素的集合,也可以是单个元素。
- 客户端(VisitorClient):它可以访问元素结构中的所有元素,并调用这些元素的操作。
代码实现
抽象访问者
这里我们定义了访问不同元素的方法。
public abstract class Visitor {
public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}具体访问者
具体访问者中可以获取到所访问元素的对象,并执行相应的操作。
public class ConcreteVisitor1 extends Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA concreteElementA) {
System.out.println("ConcreteVisitor1: ConcreteElementA visited.");
}
@Override
public void visitConcreteElementB(ConcreteElementB concreteElementB) {
System.out.println("ConcreteVisitor1: ConcreteElementB visited.");
}
}你可以仿照这段代码创建ConcreteVisitor2。
抽象元素
元素类中提供accept方法用于接收对应的访问者,即允许谁访问这个元素。
public abstract class Element {
public abstract void accept(Visitor visitor);
}具体元素
具体元素类中实现了accept方法,并将自身作为参数传递给访问者。
public class ConcreteElementA extends Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementA(this);
}
}你可以仿照这段代码创建ConcreteElementB。
对象结构
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void add(Element element) {
this.elements.add(element);
}
public void remove(Element element) {
this.elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}测试
public class VisitorClient {
public static void main(String[] args) {
Visitor visitor1 = new ConcreteVisitor1();
Visitor visitor2 = new ConcreteVisitor2();
Element elementA = new ConcreteElementA();
Element elementB = new ConcreteElementB();
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(elementA);
objectStructure.add(elementB);
objectStructure.accept(visitor1);
objectStructure.accept(visitor2);
System.out.println("===== remove elementB");
objectStructure.remove(elementB);
objectStructure.accept(visitor1);
objectStructure.accept(visitor2);
}
}结果:

访问者模式与其他模式的对比
| 模式 | 功能 | 实现方式 | 适用场景 |
|---|---|---|---|
| 访问者模式 | 分离对象结构与操作 | 双重分派机制,元素接受访问者并将自身传递给访问者 | 需要对复杂对象结构进行多种不相关操作时 |
| 迭代器模式 | 遍历集合元素 | 提供统一接口遍历集合,隐藏内部结构 | 需要遍历不同集合但保持一致接口时 |
| 策略模式 | 封装可替换算法 | 定义算法族,运行时动态切换 | 需要在运行时选择不同算法实现时 |
| 命令模式 | 封装请求为对象 | 将请求封装为命令对象,支持撤销/重做 | 需要支持请求的队列化、日志记录或撤销时 |
总结
核心思想
访问者模式的核心思想是分离对象结构与作用于结构上的操作,通过双重分派机制实现操作的动态绑定。它允许在不修改元素类的前提下,向对象结构中添加新的操作,符合开放封闭原则。
主要角色
- 抽象访问者(Visitor):定义访问各元素的接口
- 具体访问者(ConcreteVisitor):实现具体操作逻辑
- 抽象元素(Element):声明接受访问者的接口
- 具体元素(ConcreteElement):实现接受访问者的方法,将自身传递给访问者
- 对象结构(ObjectStructure):管理元素集合,提供遍历接口
- 客户端(Client):创建访问者和元素,将访问者应用于对象结构
实现方式
- 定义抽象访问者接口,包含访问各种元素的方法
- 元素类提供accept方法,接受访问者并将自身作为参数传递
- 具体访问者实现访问方法,对元素执行特定操作
- 对象结构管理元素集合,支持统一的访问者应用
优缺点
优点:
- 符合单一职责原则,操作逻辑集中在访问者中
- 符合开放封闭原则,添加新操作只需添加新访问者
- 便于实现复杂的对象结构遍历和操作组合
- 分离了对象结构与操作,提高了代码的可维护性
缺点:
- 增加了系统的复杂性,需要理解双重分派机制
- 元素类与访问者类之间存在循环依赖
- 元素结构变化时,所有访问者都需要修改
- 破坏了元素类的封装性,访问者可以访问元素的内部状态
适用场景
- 对象结构相对稳定,但需要经常添加新的操作
- 需要对复杂对象结构进行多种不相关的操作,且不希望这些操作污染元素类
- 需要在运行时根据元素类型执行不同的操作
- 希望将相关操作集中管理,便于维护
实际应用
- 编译器的语法树分析:不同访问者实现语法检查、代码生成等功能
- 文档生成器:不同访问者生成HTML、PDF等不同格式文档
- 数据库查询优化器:不同访问者实现不同的查询优化策略
- 图形界面组件的事件处理:不同访问者处理不同类型的用户事件
- 配置文件解析器:不同访问者提取配置信息的不同部分