Go中组合模式通过接口统一叶子与容器行为,用结构体嵌入和方法实现替代继承,核心是Component接口及File/Directory对其的实现,支持递归操作与类型无关调用。

在 Go 语言中实现组合模式,核心是让“单个对象”和“对象容器”统一实现同一接口,从而能以一致方式处理叶子节点与分支节点。Go 没有类继承,但通过接口 + 结构体嵌入(embedding)+ 方法实现,可以自然、简洁地表达组合关系。
定义统一的行为接口
组合模式的关键在于抽象出共同操作。例如树形结构中常见的 Print()、Count() 或 Traverse()。先定义一个接口:
type Component interface {
Print(indent string)
Count() int
}
所有叶子节点(如文件)和容器节点(如目录)都实现这个接口,调用方无需区分类型。
实现叶子节点(Leaf)
叶子节点不包含子节点,行为简单直接:
立即学习“go语言免费学习笔记(深入)”;
type File struct {
Name string
}
func (f *File) Print(indent string) {
fmt.Println(indent + "? " + f.Name)
}
func (f *File) Count() int {
return 1
}
实现容器节点(Composite)
容器持有一组 Component,自身也实现 Component 接口。利用结构体嵌入可复用通用逻辑(如子节点管理),但注意:Go 中嵌入不是继承,方法需显式转发或重写。
type Directory struct {
Name string
Children []Component // 存储任意组件:File 或其他 Directory
}
func (d *Directory) Add(c Component) {
d.Children = append(d.Children, c)
}
func (d *Directory) Print(indent string) {
fmt.Println(indent + "? " + d.Name)
for _, child := range d.Children {
child.Print(indent + " ")
}
}
func (d *Directory) Count() int {
count := 1 // 自身算一个节点
for _, child := range d.Children {
count += child.Count()
}
return count
}
这样,Directory 就成了真正的“组合体”:它既是一个组件,又能递归委托行为给子组件。
构建与使用树形结构
组合模式的价值体现在客户端代码的简洁性上:
root := &Directory{Name: "project"}
src := &Directory{Name: "src"}
mainFile := &File{Name: "main.go"}
utils := &Directory{Name: "utils"}
helper := &File{Name: "helper.go"}
utils.Add(helper)
src.Add(mainFile)
src.Add(utils)
root.Add(src)
root.Print("") // 递归打印整棵树
fmt.Println("Total nodes:", root.Count()) // 输出 5
客户端完全不用关心 src 是目录还是文件——只要它是 Component,就能调用 Print() 和 Count()。
不复杂但容易忽略:Go 中组合模式不依赖继承,而靠接口契约与结构体组合达成松耦合;子节点切片声明为 []Component 是关键,它让树具备异构性和扩展性。










