设计模式之访问数据结构_Visitor访问者模式_访问数据结构并处理数据
一、访问者模式(Visitor Pattern)
1、模式概述
访问者模式 是一种 行为型设计模式,它将 数据结构 与 作用于该结构上的操作 分离。通过引入一个 访问者(Visitor),让其“访问”结构中的每个元素,并根据元素类型执行特定操作。
核心思想:“结构稳定,操作可变 —— 新增行为不改结构。”
访问者模式 = 结构固定 + 操作外置
- 它不是“把逻辑塞进对象”,而是“让对象主动邀请外部逻辑来处理自己”。
类比理解:
- 你在朋友家做客(你是 访问者);
- 朋友向你展示他的收藏(数据结构);
- 你根据每件物品(元素)做出不同评价(处理逻辑)。
- 朋友不需要知道你会怎么评价,只需“接受你的访问”即可。
2、使用场景
访问者模式适用于以下情况:
- 不希望 污染元素类 的代码(避免在每个元素中写大量业务逻辑);
- 需要对 一组不同类型的对象 执行 相同的操作流程,但具体行为因类型而异;
- 符合 开闭原则:新增操作只需添加新访问者,无需修改现有元素类。
📌 典型应用:
- 编译器中的语法树遍历(AST Visitor);
- 报表生成(同一数据结构 → HTML / PDF / Excel 多种输出);
- 游戏中对不同单位(士兵、建筑、资源)执行统一 AI 行为;
- 电商订单系统中对商品、优惠券、运费等组件进行统一计价。
4、示例程序:电脑部件 + 多种访问者
1)元素接口 ComputerPart
/**
* 元素接口(Element)
* 所有可被访问的部件都需实现此接口
*/
public interface ComputerPart {
void accept(ComputerPartVisitor visitor);
}
2)具体元素(ConcreteElement)
a、CPU
public class CPU implements ComputerPart {
@Override
public void accept(ComputerPartVisitor visitor) {
visitor.visit(this); // 双重分发:调用 visitor.visit(CPU)
}
}
b、HardDisk
public class HardDisk implements ComputerPart {
@Override
public void accept(ComputerPartVisitor visitor) {
visitor.visit(this);
}
}
c、Monitor
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor visitor) {
visitor.visit(this);
}
}
3)访问者接口 ComputerPartVisitor
- 接口方法 按具体类型重载,而非泛型或 Object;
- 利用编译时类型信息,实现精准分发。
/**
* 访问者接口(Visitor)
* 为每种具体元素声明 visit 方法
*/
public interface ComputerPartVisitor {
void visit(CPU cpu);
void visit(HardDisk hardDisk);
void visit(Monitor monitor);
}
4)具体访问者(ConcreteVisitor)
a、显示访问者
/**
* 具体访问者:显示部件信息
*/
public class DisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(CPU cpu) {
System.out.println("Displaying CPU");
}
@Override
public void visit(HardDisk hardDisk) {
System.out.println("Displaying Hard Disk");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor");
}
}
b、价格计算访问者
/**
* 具体访问者:计算总价格
*/
public class PriceCalculator implements ComputerPartVisitor {
private double total = 0;
@Override
public void visit(CPU cpu) {
total += 3000;
System.out.println("CPU added: ¥3000");
}
@Override
public void visit(HardDisk hardDisk) {
total += 800;
System.out.println("Hard Disk added: ¥800");
}
@Override
public void visit(Monitor monitor) {
total += 1500;
System.out.println("Monitor added: ¥1500");
}
public double getTotal() {
return total;
}
}
5)对象结构(可选)Computer
import java.util.Arrays;
import java.util.List;
/**
* 对象结构(ObjectStructure)
* 管理所有部件,并提供统一访问入口
*/
public class Computer {
private final List<ComputerPart> parts = Arrays.asList(
new CPU(),
new HardDisk(),
new Monitor()
);
public void accept(ComputerPartVisitor visitor) {
for (ComputerPart part : parts) {
part.accept(visitor);
}
}
}
6)客户端测试 Main
- 新增操作(如
PowerConsumptionVisitor)无需修改任何部件类; - 同一结构支持 无限种访问行为;
- 逻辑集中,易于维护。
public class Main {
public static void main(String[] args) {
Computer computer = new Computer();
// 场景1:显示所有部件
System.out.println("=== 显示部件 ===");
computer.accept(new DisplayVisitor());
// 场景2:计算总价
System.out.println("\n=== 计算价格 ===");
PriceCalculator calculator = new PriceCalculator();
computer.accept(calculator);
System.out.println("总价: ¥" + calculator.getTotal());
}
}
输出:
=== 显示部件 ===
Displaying CPU
Displaying Hard Disk
Displaying Monitor
=== 计算价格 ===
CPU added: ¥3000
Hard Disk added: ¥800
Monitor added: ¥1500
总价: ¥5300
7)模式角色
| 角色 | 职责 | 示例 |
|---|---|---|
| Visitor(访问者接口) | 声明访问各元素的方法 | ComputerPartVisitor |
| ConcreteVisitor(具体访问者) | 实现访问者接口,定义具体操作 | DisplayVisitor, PriceCalculator |
| Element(元素接口) | 声明接受访问者的方法 | ComputerPart |
| ConcreteElement(具体元素) | 实现 Element 接口,调用访问者的对应方法 | CPU, HardDisk, Monitor |
| ObjectStructure(对象结构) | 管理元素集合,提供高层遍历接口(可选) | Computer |
┌──────────────────────┐
│ ComputerPart │
│----------------------│
│ + accept(visitor) │
└──────────▲───────────┘
│
┌─────────────────────┴─────────────────────┐
│ │
┌────▼─────┐ ┌───────────▼───────────┐ ┌─────▼──────┐
│ CPU │ │ HardDisk │ │ Monitor │
└──────────┘ └───────────────────────┘ └────────────┘
┌──────────────────────┐
│ ComputerPartVisitor │
│----------------------│
│ + visit(CPU) │
│ + visit(HardDisk) │
│ + visit(Monitor) │
└──────────▲───────────┘
│
┌───────────────────┴───────────────────┐
│ │
┌──────▼────────┐ ┌──────────▼───────────┐
│ DisplayVisitor│ │ PriceCalculator │
└───────────────┘ └──────────────────────┘
5、FQA
1) 模式优点
- 符合开闭原则:新增操作只需添加新访问者;
- 职责分离:元素只负责“被访问”,操作逻辑集中在访问者;
- 易于增加新操作:特别适合“操作多、结构稳定”的系统。
2)注意事项
- 破坏封装性:访问者需了解元素内部细节;
- 难以扩展元素:新增元素类型需修改所有访问者接口(违反开闭原则);
- 适用范围有限:仅当 结构稳定、操作多变 时才推荐使用。


