Visitor 模式在 Go 中用于分离算法与对象结构,通过定义 Element 接口和 Visitor 接口实现对文件系统等复合结构的遍历操作。示例中 File 和 Directory 实现 Accept 方法,分别接受 PrintVisitor 打印名称和 SizeVisitor 统计大小,输出显示遍历结果与总大小 350 字节,适用于操作多类型对象且需扩展新行为的场景。

在 Go 语言中,Visitor 模式常用于对一组结构不同的对象进行统一操作,而又不改变它们的结构。它将算法与对象结构分离,通过“访问者”来定义作用于元素的新操作。
Visitor 模式核心结构
Visitor 模式通常包含以下部分:
- Element 接口:定义 Accept 方法,接受一个访问者。
- ConcreteElement:具体元素类型,实现 Accept 方法。
- Visitor 接口:定义 Visit 方法,对应不同元素类型。
- ConcreteVisitor:具体访问者,实现对每种元素的操作。
示例:文件系统遍历
假设我们有一个简单的文件系统结构,包含文件和目录,想对它们分别执行“打印名称”和“统计大小”的操作。使用 Visitor 模式可以解耦数据结构与行为。
package main
import "fmt"
// Element 接口
type FileSystemElement interface {
Accept(visitor Visitor)
}
// 文件结构
type File struct {
Name string
Size int
}
func (f *File) Accept(visitor Visitor) {
visitor.VisitFile(f)
}
// 目录结构
type Directory struct {
Name string
Elements []FileSystemElement
}
func (d *Directory) Accept(visitor Visitor) {
visitor.VisitDirectory(d)
for _, e := range d.Elements {
e.Accept(visitor) // 递归访问子元素
}
}
// Visitor 接口
type Visitor interface {
VisitFile(*File)
VisitDirectory(*Directory)
}
// 打印访问者
type PrintVisitor struct{}
func (v *PrintVisitor) VisitFile(f *File) {
fmt.Printf("文件: %s\n", f.Name)
}
func (v *PrintVisitor) VisitDirectory(d *Directory) {
fmt.Printf("目录: %s\n", d.Name)
}
// 统计大小访问者
type SizeVisitor struct {
TotalSize int
}
func (v *SizeVisitor) VisitFile(f *File) {
v.TotalSize += f.Size
}
func (v *SizeVisitor) VisitDirectory(d *Directory) {
// 目录本身不占空间,可忽略或加固定开销
}
func main() {
root := &Directory{
Name: "根目录",
Elements: []FileSystemElement{
&File{Name: "a.txt", Size: 100},
&File{Name: "b.go", Size: 200},
&Directory{
Name: "子目录",
Elements: []FileSystemElement{
&File{Name: "c.txt", Size: 50},
},
},
},
}
// 使用打印访问者
printVisitor := &PrintVisitor{}
fmt.Println("=== 打印文件结构 ===")
root.Accept(printVisitor)
// 使用统计大小访问者
sizeVisitor := &SizeVisitor{}
fmt.Println("\n=== 统计总大小 ===")
root.Accept(sizeVisitor)
fmt.Printf("总大小: %d 字节\n", sizeVisitor.TotalSize)
}
输出结果
运行上述代码会得到:
立即学习“go语言免费学习笔记(深入)”;
=== 打印文件结构 === 目录: 根目录 文件: a.txt 文件: b.go 目录: 子目录 文件: c.txt === 统计总大小 === 总大小: 350 字节
优点与适用场景
Visitor 模式适合以下情况:
- 需要对多种类型的对象执行不同操作,且操作频繁变化。
- 希望避免在对象结构中添加大量逻辑代码。
- 需要集中管理某类算法,比如序列化、渲染、分析等。
Go 语言没有方法重载,因此通过接口和多态实现访问者分发是常见做法。虽然写法略显冗长,但结构清晰,扩展性强。
基本上就这些。










