访问者模式是 23 种里比较冷门的一个:把"操作"从"数据结构"里分离出来,加新操作不改数据结构。它用在"数据结构稳定、操作多变"的场景(如编译器 AST)。这篇讲清它的思路和代价。
一、解决什么问题:加操作不改数据结构
有一组稳定的数据结构(比如文件、文件夹),但要对它们做很多不同操作(统计大小、统计数量、打印……)。
不用访问者:每个操作都要写进每个数据结构类里,加一个操作就要改所有数据结构类。
访问者模式:把操作独立成"访问者",数据结构只提供一个"接受访问"的方法,加新操作 = 加一个新访问者,数据结构不动。
二、核心结构
java
// 数据结构:元素,都实现"接受访问者"
interface Element {
void accept(Visitor visitor); // 接受访问,把自己交给访问者
}
class File implements Element {
private int size;
public int getSize() { return size; }
public void accept(Visitor v) { v.visit(this); } // 回调访问者的 visit(File)
}
class Folder implements Element {
private List<Element> children;
public void accept(Visitor v) { v.visit(this); } // 回调 visit(Folder)
}
// 访问者:操作独立出来,用重载区分不同类型的元素
interface Visitor {
void visit(File file);
void visit(Folder folder);
}
// 具体访问者:统计大小
class SizeVisitor implements Visitor {
private int total = 0;
public void visit(File file) { total += file.getSize(); }
public void visit(Folder folder) { /* 递归统计子元素 */ }
public int getTotal() { return total; }
}关键:加新操作(比如"打印所有文件名")= 加一个新 Visitor,File/Folder 的代码完全不动。这是访问者的核心价值。
三、代价:为什么冷门
访问者有个明显缺点:数据结构一变,所有访问者都要改(因为 Visitor 接口要对每个元素类型写一个 visit 方法)。
所以它只适合"数据结构稳定、操作频繁变"的场景。实际业务里这种场景少,所以冷门。
四、实际应用
- 编译器:AST(抽象语法树)结构稳定,但要做类型检查、代码生成、优化等多种操作
- 报表系统:数据结构固定,但要生成各种报表
小结
- 访问者把"操作"从"数据结构"分离,加操作不改数据结构
- 代价:数据结构一变,所有访问者都要改,所以只适合"结构稳定、操作多变"
- 典型:编译器 AST
相关:《组合模式》(访问者常配合树形结构(组合)遍历)。
