访问者模式 (Visitor)
📖 通俗理解
年终考核:
- HR 来了:评估员工的考勤、态度
- 财务来了:核算员工的工资、奖金
- CTO 来了:评估员工的技术能力
不同的"访问者"对员工进行不同维度的评估,而不需要修改员工类。
访问者模式就是:在不改变元素类的前提下,定义新的操作。
🎯 解决什么问题?
当需要对一组对象执行不同的操作,且不想修改这些对象的类时。
🌰 生活中的例子
- 年终考核:不同部门的人考核不同维度
- 税务检查:对不同资产有不同的检查方式
- 编译器:对 AST 节点进行不同操作(类型检查、代码生成)
💻 Java 代码实现
场景:员工绩效考核
/**
* 访问者接口
*/
public interface Visitor {
void visit(Engineer engineer);
void visit(Manager manager);
}
/**
* 员工接口
*/
public interface Employee {
void accept(Visitor visitor);
}
/**
* 工程师
*/
public class Engineer implements Employee {
private String name;
private int codeLines;
public Engineer(String name, int codeLines) {
this.name = name;
this.codeLines = codeLines;
}
public String getName() { return name; }
public int getCodeLines() { return codeLines; }
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 经理
*/
public class Manager implements Employee {
private String name;
private int projectCount;
public Manager(String name, int projectCount) {
this.name = name;
this.projectCount = projectCount;
}
public String getName() { return name; }
public int getProjectCount() { return projectCount; }
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* KPI 考核访问者
*/
public class KPIVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师 " + engineer.getName() +
" 代码量: " + engineer.getCodeLines() + " 行");
}
@Override
public void visit(Manager manager) {
System.out.println("经理 " + manager.getName() +
" 管理项目数: " + manager.getProjectCount() + " 个");
}
}
/**
* 财务审计访问者
*/
public class FinanceVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
double salary = engineer.getCodeLines() * 0.1;
System.out.println("工程师 " + engineer.getName() +
" 绩效工资: ¥" + salary);
}
@Override
public void visit(Manager manager) {
double bonus = manager.getProjectCount() * 1000;
System.out.println("经理 " + manager.getName() +
" 项目奖金: ¥" + bonus);
}
}
使用:
public class Client {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Engineer("张三", 5000),
new Engineer("李四", 8000),
new Manager("王总", 3)
);
System.out.println("=== KPI 考核 ===");
Visitor kpiVisitor = new KPIVisitor();
for (Employee e : employees) {
e.accept(kpiVisitor);
}
System.out.println("\n=== 财务审计 ===");
Visitor financeVisitor = new FinanceVisitor();
for (Employee e : employees) {
e.accept(financeVisitor);
}
}
}
输出:
=== KPI 考核 ===
工程师 张三 代码量: 5000 行
工程师 李四 代码量: 8000 行
经理 王总 管理项目数: 3 个
=== 财务审计 ===
工程师 张三 绩效工资: ¥500.0
工程师 李四 绩效工资: ¥800.0
经理 王总 项目奖金: ¥3000.0
⚠️ 双重分派
访问者模式使用了"双重分派"技术:
- 第一次分派:调用
element.accept(visitor),根据 element 类型选择方法 - 第二次分派:调用
visitor.visit(this),根据 visitor 类型选择方法
✅ 适用场景
- 对象结构稳定,但经常需要新增操作
- 需要对一组对象执行不同的操作
- 数据结构与操作分离
⚠️ 优缺点
优点:
- 新增操作容易(新增 Visitor)
- 相关操作集中在一个 Visitor 中
缺点:
- 新增元素困难(需要修改所有 Visitor)
- 破坏封装(Visitor 需要访问元素内部状态)
小结
访问者模式:在不修改元素类的情况下,定义新的操作。
适合:操作经常变化,但数据结构稳定的场景。
应用:编译器 AST 处理、文档转换、复杂对象结构的批量操作
🎉 恭喜你完成了 23 种设计模式的学习!
👉 返回首页
