访问者模式
访问者模式用于在不修改对象结构的情况下,给一组对象增加新的操作。
一句话理解:对象结构稳定,但操作经常增加,就把操作放到访问者里。
解决什么问题
假设有一组固定元素:
text
普通员工
经理
实习生现在需要对这些元素做不同操作:
- 计算薪资。
- 导出报表。
- 生成绩效。
- 做权限检查。
如果每新增一种操作都去修改员工类,会违反开闭原则。访问者模式把操作抽离成访问者。
基本结构
text
Element.accept(Visitor)
Visitor.visit(ConcreteElement)角色:
Visitor:访问者接口,定义访问不同元素的方法。ConcreteVisitor:具体操作。Element:元素接口,提供accept。ConcreteElement:具体元素。
Java 示例
元素接口:
java
interface Employee {
void accept(EmployeeVisitor visitor);
}具体元素:
java
class Engineer implements Employee {
private final int codeLines;
Engineer(int codeLines) {
this.codeLines = codeLines;
}
public int codeLines() {
return codeLines;
}
public void accept(EmployeeVisitor visitor) {
visitor.visit(this);
}
}
class Manager implements Employee {
private final int productCount;
Manager(int productCount) {
this.productCount = productCount;
}
public int productCount() {
return productCount;
}
public void accept(EmployeeVisitor visitor) {
visitor.visit(this);
}
}访问者接口:
java
interface EmployeeVisitor {
void visit(Engineer engineer);
void visit(Manager manager);
}具体访问者:
java
class ReportVisitor implements EmployeeVisitor {
public void visit(Engineer engineer) {
System.out.println("engineer code lines: " + engineer.codeLines());
}
public void visit(Manager manager) {
System.out.println("manager product count: " + manager.productCount());
}
}使用:
java
import java.util.Arrays;
import java.util.List;
List<Employee> employees = Arrays.asList(
new Engineer(2000),
new Manager(3)
);
EmployeeVisitor visitor = new ReportVisitor();
for (Employee employee : employees) {
employee.accept(visitor);
}使用场景
- 对象结构稳定,操作经常增加。
- 编译器 AST 遍历。
- 报表导出。
- 复杂对象结构统计。
- 不同类型节点执行不同操作。
优点
- 新增操作比较方便,只要新增访问者。
- 把操作逻辑集中在访问者中。
- 适合复杂对象结构的遍历和统计。
缺点
- 新增元素类型很麻烦,需要修改所有访问者。
- 破坏一定封装性,访问者常常需要读取元素内部数据。
- 代码理解门槛较高。
- 普通业务系统中使用频率不高。
双分派
访问者模式的核心是双分派:
text
第一次分派:employee.accept(visitor)
第二次分派:visitor.visit(this)通过运行时元素类型,调用到对应的 visit 方法。
实战注意
如果元素类型经常变化,不适合访问者模式。如果操作经常变化,而元素结构长期稳定,访问者才有价值。