首页/开发/代码质量与评审速查/访问者模式 Visitor(行为型)
设计原则与模式62 条

访问者模式 Visitor(行为型)

把作用于对象结构的操作从元素类中分离:元素接受访问者,操作以访问者形式新增,元素类无需改动;元素类型频繁新增时此模式成本较高。

访问者模式解决什么

访问者模式(Visitor)把作用于一组对象的操作从这些对象的类里分离出来:元素类提供一个接受访问者的入口,具体操作写在访问者里,新增一种操作只需新增一个访问者,元素类不用改动。它的实现基础是双重分派——先由元素决定调用哪个接受方法,再由传入的访问者决定执行哪个处理逻辑。这个取舍有方向:操作容易增加、元素类型相对稳定时收益最大。

基本信息

意图操作与对象结构分离,用双重分派选择处理逻辑
适合元素类型稳定,而操作种类持续增加
代价新增一种元素类型需要修改所有访问者
典型位置语法树处理、报表导出、规则校验

典型使用场景

  • 语法树处理:解析结果结构固定,而类型检查、优化、代码生成等操作不断增加
  • 报表导出:节点类型稳定,导出为不同格式的需求陆续出现
  • 规则校验:对同一对象结构套用多套校验规则,规则之间互不影响
  • 静态分析:遍历同一棵结构,分别做统计、告警与指标提取
  • 文档结构转换:同一种节点结构输出为多种目标格式

结构示意

结构示意
// 元素侧:声明接受访问者,把分派交给访问者
interface Shape {
    void accept(Visitor v);
}

// 访问者侧:每种元素类型一个处理方法
interface Visitor {
    void visit(Circle c);
    void visit(Square s);
}

class AreaCalculator implements Visitor { /* 面积计算 */ }

适用与代价

维度有利情形不利情形
元素类型类型稳定,短期不新增类型频繁增加,每次都要改全部访问者
操作数量操作种类多且持续增加只有一两种操作,直接写在元素里更简单
结构访问需要遍历结构并保持封装遍历逻辑本身很复杂,访问者会变得庞大
状态携带访问者内可累积统计结果访问者持有大量跨元素状态,难以复用
访问者最容易失控的地方是依赖元素内部状态:一旦访问者需要读取元素的私有细节,封装就被穿透,元素与访问者双向耦合,改动又会互相牵连。
元素类型预计会频繁增加时不要选访问者:访问者要求所有元素类型的处理分支集中定义,新增类型意味着改动全部访问者,此时用多态方法或模式匹配更合适。

落地检查清单

  • 元素类型是否足够稳定,能承受访问者接口的改动成本
  • 访问者只依赖元素的公开能力,不穿透内部状态
  • 新增一种操作时元素类无需改动
  • 访问者职责单一,不为复用而累积大量跨元素状态

易错点

  • 元素类型频繁增加:每新增一种元素都要改所有访问者,扩展成本随时间上升
  • 访问者依赖元素内部状态:封装被穿透,元素与访问者双向耦合,改动互相牵连
  • 用访问者承载过多操作:单个访问者类不断膨胀,最终比直接写在元素里更难维护
  • 只有一两种操作就引入访问者:多出一层分派与接口,阅读时需要跨文件追踪调用

数据来源与参考

  • 《设计模式》(GoF)· Visitor
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌