访问者模式通过接口和组合在Go中实现,将数据结构与操作分离,适用于文件系统遍历等需对稳定结构执行多操作的场景。

访问者模式是一种行为设计模式,它允许你在不修改对象结构的情况下定义新的操作。在 Go 语言中,虽然没有继承和重载支持,但通过接口和组合可以很好地实现访问者模式。
访问者模式的核心思想
将数据结构与作用于其上的操作分离,使得可以在不改变结构的前提下添加新操作。这特别适用于需要对一组复杂对象结构执行多种不同操作的场景。
主要角色包括:
- 元素(Element):定义一个 Accept 方法,接收一个访问者对象
- 访问者(Visitor):定义一系列 Visit 方法,对应不同的元素类型
- 具体元素(ConcreteElement):实现 Accept 方法,调用访问者的 Visit 方法
- 具体访问者(ConcreteVisitor):实现访问者的接口,完成具体逻辑
Go 中的实现方式
由于 Go 不支持方法重载,我们不能像 Java 那样为不同类型的元素定义同名但参数不同的 Visit 方法。解决方案是使用接口和类型断言,或通过统一的访问者接口配合类型判断。
立即学习“go语言免费学习笔记(深入)”;
示例:文件系统遍历
package main
import "fmt"
// Visitor 定义访问者接口
type Visitor interface {
VisitFile(*File)
VisitDirectory(*Directory)
}
// Element 定义可访问元素接口
type Element interface {
Accept(Visitor)
}
// File 文件类
type File struct {
Name string
Size int
}
func (f *File) Accept(v Visitor) {
v.VisitFile(f)
}
// Directory 目录类
type Directory struct {
Name string
Children []Element
}
func (d *Directory) Accept(v Visitor) {
v.VisitDirectory(d)
for _, child := range d.Children {
child.Accept(v)
}
}
// PrintVisitor 打印访问者
type PrintVisitor struct{}
func (v *PrintVisitor) VisitFile(f *File) {
fmt.Printf("文件: %s (%d字节)\n", f.Name, f.Size)
}
func (v *PrintVisitor) VisitDirectory(d *Directory) {
fmt.Printf("目录: %s\n", d.Name)
}
// SizeVisitor 统计大小访问者
type SizeVisitor struct {
TotalSize int
}
func (v *SizeVisitor) VisitFile(f *File) {
v.TotalSize += f.Size
}
func (v *SizeVisitor) VisitDirectory(d *Directory) {
// 目录本身不计入大小
}
func (v *SizeVisitor) GetTotal() int {
return v.TotalSize
}使用示例
构建一个简单的文件树并应用不同的访问者:
func main() {
root := &Directory{
Name: "根目录",
Children: []Element{
&File{Name: "readme.txt", Size: 1024},
&Directory{
Name: "子目录",
Children: []Element{
&File{Name: "main.go", Size: 2048},
&File{Name: "config.json", Size: 512},
},
},
},
}
// 使用打印访问者
fmt.Println("=== 打印文件结构 ===")
printer := &PrintVisitor{}
root.Accept(printer)
// 使用统计访问者
fmt.Println("\n=== 计算总大小 ===")
sizeVisitor := &SizeVisitor{}
root.Accept(sizeVisitor)
fmt.Printf("总大小: %d 字节\n", sizeVisitor.GetTotal())
}注意事项与适用场景
Go 的访问者模式实现依赖接口和多态,关键在于合理设计 Element 和 Visitor 接口。
- 当新增元素类型时,所有现有访问者都需要更新接口实现
- 适合数据结构相对稳定,但操作频繁变化的场景
- 常见应用场景包括:语法树分析、对象序列化、UI 渲染等
- 避免过度设计,简单场景直接用函数更清晰
基本上就这些。Go 虽然语法简洁,但通过接口依然能优雅实现经典设计模式。访问者模式帮助你解耦数据与行为,让代码更具扩展性。










