关于 gof-visitor
gof-visitor 是面向开发者的 GoF Visitor 模式技能,讲解如何为稳定的对象层次添加操作,并说明双重分派、权衡及何时应选择穷尽式模式匹配。
主要功能
- 01Visitor 与元素的职责划分
- 02双重分派机制说明
- 03选择依据与权衡
- 04反模式及替代方案
适用场景
- 01为 AST 添加序列化器或检查器
- 02对类型层次执行多种操作
- 03比较 Visitor 与 sealed 模式匹配
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ yonatankarp/ software-design-skills --skill gof-visitor安装后在 Claude Code 中输入/gof-visitor确认已加载。
详细介绍
一句话总结
表示对稳定对象结构中的元素执行的操作——让你无需修改元素类,就能向该结构添加新操作。
何时使用此技能
- 需要随着时间推移为稳定的类型层次结构(AST 节点、文件系统条目、文档元素)添加新操作,而且你不想反复修改每个元素类。
- 需要对同一层次结构执行不同操作:美化打印、类型检查、JSON 序列化、指标收集。每种操作都是一个单独的 Visitor。
- 经典的 GoF“双重分派”用例——根据两种动态类型(元素类型和操作类型)选择要调用的方法。
何时不应使用此技能
- 层次结构本身并不稳定——每增加一种元素类型,就必须更新每个 Visitor。Visitor 以“低成本添加操作”为代价,换来“高成本添加元素类型”。
- 语言已经提供穷尽式模式匹配(Kotlin 对
sealed类型使用when、Rust 的match、Scala 模式匹配)。在这些语言中,你可以将操作编写为带穷尽式when的函数,无需让每个元素都继承某个类。 - 你只需要添加一个操作,而且没有理由预期还会添加更多操作。直接在元素上添加方法更简单。
核心内容
由两部分组成:
元素层次结构。 每个元素类都有一个 accept(visitor) 方法,会将自身回调给 Visitor:accept(v) { v.visitX(this) }。元素不会为该操作执行其他操作。
Visitor 接口。 为每种具体元素类型声明一个方法:visitCircle(Circle)、visitSquare(Square) 等。每个具体 Visitor(美化打印器、类型检查器、JSON 序列化器)都实现所有这些方法。
添加新的操作意味着添加一个新的 Visitor 类。现有元素类无需改动。
双重分派的技巧:客户端代码调用 element.accept(visitor) 时,第一次分派会选择要运行哪个 accept(根据元素的运行时类型),在其中,第二次分派会选择要运行哪个 visitX(根据 Visitor 的运行时类型)。两层多态协同工作。
在 Kotlin 中,这通常没有必要。 sealed 层次结构加上穷尽式 when,能带来同样的“无需改动元素类即可添加新操作”的好处,不必使用 accept 这种繁琐写法,而且编译器会强制你处理每种元素类型:
sealed class Shape
data class Circle(...): Shape()
data class Square(...): Shape()
fun area(s: Shape): Double = when (s) {
is Circle -> ...
is Square -> ...
} // compile error if a new Shape variant isn't handled
权衡关系正好相反:使用 sealed when 时,添加操作的成本很低(只需新增一个函数),而添加元素则需要大范围修改(必须更新每个现有的 when——不过编译器会逐一指出)。
决策启发
- 当语言不支持对封闭层次结构进行穷尽式模式匹配时,选择 Visitor。
- 在 Kotlin(及类似语言)中,优先使用
sealed+ 穷尽式when——既能获得同样的好处,又更简洁,并且有编译时穷尽性检查。 - 当操作需要在不同元素类型之间协同工作时,使用 Visitor(例如在遍历树时维护状态的树遍历器)——与临时函数相比,Visitor 能更清晰地在多次访问之间保留状态。
反模式
- 在不稳定的层次结构中使用 Visitor——每增加一种元素类型,就必须更新 N 个 Visitor;而在没有显式声明覆盖检查的语言中,编译器也帮不上忙。
- 使用带默认空操作方法的 Visitor 来“简化添加元素”——会悄无声息地漏掉无人记得处理的类型所需的行为。
- 在支持穷尽式模式匹配的语言中使用 Visitor——徒增繁琐,却没有任何好处。
另请参阅
gof-iterator— Visitor 用于遍历结构;现代替代方案通常是使用生成元素的 Iterator,配合穷尽式when。gof-composite— Visitor 常用于遍历 Composite;accept的递归会向下遍历树。gof-kotlin-idioms— 在 Kotlin 中,sealed 类 +when是 Visitor 的惯用替代方案。
参考资料
- 设计模式(GoF,1994),第 5 章——访问者。(未收录于《HFDP》第 2 版。)
- Kotlin 设计模式与最佳实践(Alexey Soshin),第 4 章——访问者,其中包含密封类对比。
评论
支持 Markdown