3.12 访问者模式
访问者模式(Visitor)让你在不修改对象结构里各元素类的前提下,为它们新增操作。它把“操作”从元素身上抽离,搬进独立的访问者对象。当你有一个稳定的对象结构,却要不断给它加新功能时,访问者大显身手。
下面的实验:一组图形(圆、正方形、矩形)保持不变,切换“面积访问者 / 周长访问者”,就能对所有图形施加全新的算法。
正在加载交互实验...
双分派:accept 与 visit
访问者依赖一个叫双分派(double dispatch)的技巧:
java
interface Visitor {
void visit(Circle c);
void visit(Square s);
}
interface Shape { void accept(Visitor v); }
class Circle implements Shape {
public void accept(Visitor v) { v.visit(this); } // 回调,带上自己的真实类型
}调用 shape.accept(visitor) 时,先由 shape 的真实类型决定进入哪个 accept,再由它调用 visitor.visit(this)——this 的静态类型已经确定,于是选中正确的 visit 重载。两次分派合起来,让“元素类型 × 操作类型”被精确匹配。
下面的导出实验:同一份文档结构(标题、段落、图片),换一个访问者就能输出 HTML 或 Markdown。
正在加载交互实验...
正在加载概念检查...
它的甜区与硬伤
访问者有一个非常明确的非对称权衡:
- 擅长新增操作:要给结构加一个新算法?写一个新访问者即可,元素类一行不改。
- 不擅长新增元素类型:要加一种新元素(比如新增
Triangle)?你必须去修改每一个访问者接口和实现,给它加visit(Triangle)。
所以判断准则很清晰:元素类型稳定、操作经常变 → 用访问者;反之(元素常变、操作稳定)→ 访问者会让你苦不堪言,普通多态方法更好。
正在加载概念检查...
现实身影
编译器对 AST 的多趟处理(类型检查、优化、代码生成各是一个访问者)、文档/UI 树的多格式导出、对象图的序列化,都是访问者的经典战场。
正在加载本节练习...