跳转到主内容
极星编程网:以代码为星,赴技术山海!

Golang访问者模式如何新增操作不改结构_扩展性设计说明

新增操作时不能改Element接口,因其违反开闭原则;应新增Visitor实现类(如MarkdownVisitor),通过VisitFile/VisitDirectory方法封装逻辑,保持Element结构稳定。 新增操作时为什么不能改 Element 接口 访问者模式的核心契约是:
Element
接口只定义
Accept(visitor Visitor)
方法,所有具体元素(如
File
、
Directory
)都必须实现它。一旦你为某个具体元素类型新增一个操作(比如「计算校验和」),却去修改
File
或
Directory
的结构(例如加新方法),就破坏了开闭原则——后续每加一个操作都要动已有类,可维护性立刻崩塌。 真正该做的,是新增一个实现了
Visitor
接口的新类型,比如
ChecksumVisitor
,然后在它的
VisitFile
和
VisitDirectory
方法里写逻辑。 Visitor 接口如何设计才支持无侵入扩展 关键在于:Visitor 接口的方法签名必须覆盖全部 Element 类型,且方法名要能体现“被访问对象”的语义,而不是操作意图。否则每次加新 Element 类型(比如加个
Symlink
),就得改 Visitor 接口,又违反开闭。
type Visitor interface { VisitFile(*File) VisitDirectory(*Directory) // 不要叫 VisitForBackup() 或 VisitForLog() —— 这是操作意图,会随业务变 // 也不要漏掉未来可能的类型,宁可预留 VisitSymlink(*Symlink) 并空实现 }
常见错误: 立即学习 “ go语言免费学习笔记(深入) ”; 把
Visitor
设计成泛型接口(如
Visitor[T any]
),导致每个新操作都要声明新接口,无法统一调度 用反射动态调用 Visit 方法——失去编译期检查,运行时报错难定位 在
Accept
里硬 编码 switch type,绕过 Visitor 接口,等于没用模式 实际新增一个操作:生成 Markdown 目录树 假设已有
File
和
Directory
,现在要支持「输出为 Markdown 格式目录结构」。不改任何现有 Element 实现,只加: React Native For Android 源码编译 中文WORD版 本文档主要讲述的是React Native For Android 源码编译;希望对大家会有帮助;感兴趣的朋友可以过来看看 下载
type MarkdownVisitor struct { buf strings.Builder indent int }

func (v MarkdownVisitor) VisitFile(f File) { v.buf.WriteString(strings.Repeat(" ", v.indent)) v.buf.WriteString("- ? ") v.buf.WriteString(f.Name) v.buf.WriteString("\n") }

func (v MarkdownVisitor) VisitDirectory(d Directory) { v.buf.WriteString(strings.Repeat(" ", v.indent)) v.buf.WriteString("- ? ") v.buf.WriteString(d.Name) v.buf.WriteString("\n") v.indent++ for _, child := range d.Children { child.Accept(v) // 注意:这里依赖每个 child 都实现了 Accept } v.indent-- }

func (v *MarkdownVisitor) Result() string { return v.buf.String() }

调用方式干净利落:
visitor := &MarkdownVisitor{} root.Accept(visitor) fmt.Println(visitor.Result())
这个过程没碰
File
、
Directory
、
Visitor
接口一行代码。 容易被忽略的边界:循环引用与访问深度控制 真实文件系统可能有符号链接甚至硬链接环,
Accept
递归调用时若不做防环处理,会栈溢出。这不是 Visitor 模式本身的问题,但扩展新 Visitor 时极易忽略。 建议在 Visitor 实现里自带访问路径记录(比如
map[uintptr]bool
记住已访问对象地址),或传入上下文控制最大深度: 在
VisitDirectory
开头加
if depth > maxDepth { return }
避免在
Accept
方法里直接递归调用自身,而是由 Visitor 主动决定是否深入子节点 如果 Element 结构本身支持 ID(如 i node ),Visitor 可以用
map[uint64]bool
去重 没有统一的「安全访问器基类」,每个新 Visitor 都得自己考虑这点——这是扩展性代价中最容易漏掉的一块。

相关文章