mcpField
G
gof-visitor

gof-visitor

github.com/yonatankarp开发143 次安装23 周前更新
代码仓库

关于 gof-visitor

gof-visitor 是面向开发者的 GoF Visitor 模式技能,讲解如何为稳定的对象层次添加操作,并说明双重分派、权衡及何时应选择穷尽式模式匹配。

主要功能

  1. 01Visitor 与元素的职责划分
  2. 02双重分派机制说明
  3. 03选择依据与权衡
  4. 04反模式及替代方案

适用场景

  1. 01为 AST 添加序列化器或检查器
  2. 02对类型层次执行多种操作
  3. 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