组合模式(Composite Design)跟我们之前讲的面向对象设计中的“组合关系(通过组合来组装两个类)”,完全是两码事。这里讲的“组合模式”,主要是用来处理树形结构数据。这里的“数据”,你可以简单理解为一组对象集合,待会我们会详细讲解。
正因为其应用场景的特殊性,数据必须能表示成树形结构,这也导致了这种模式在实际的项目开发中并不那么常用。但是,一旦数据满足树形结构,应用这种模式就能发挥很大的作用,能让代码变得非常简洁。
1. 组合模式的原理
将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让使用者可以统一单个对象和组合对象的处理逻辑。
Compose objects into tree structure to represent part-whole hierarchies.Composite lets client treat individual objects and compositions of objects uniformly.
以文件和目录为例。将一组对象(文件和目录)组织成树形结构,以表示一种‘部分 - 整体’的层次结构(目录与子目录的嵌套结构)。组合模式让客户端可以统一单个对象(文件)和组合对象(目录)的处理逻辑(递归遍历)。
这种组合模式的设计思路,与其说是一种设计模式,倒不如说是对业务场景的一种数据结构和算法的抽象。其中,数据可以表示成树这种数据结构,业务需求可以通过在树上的递归遍历算法来实现。
2. 组合模式的应用场景
组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看做树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。
典型例子:文件与目录、部分与员工。
3. 组合模式的代码实现
type FileSystemNode interface {
search(keyword string)
}
type File struct {
name string
}
func (f *File) search(keyword string) {
fmt.Printf("Searching for keyword %s in file %s\n", keyword, f.name)
}
type Folder struct {
name string
nodes []FileSystemNode
}
func (f *Folder) addNode(node FileSystemNode) {
f.nodes = append(f.nodes, node)
}
func (f *Folder) search(keyword string) {
fmt.Printf("Serching recursively for keyword %s in folder %s\n", keyword, f.name)
for _, node := range f.nodes {
node.search(keyword)
}
}
// 客户端代码
func TestNode(t *testing.T) {
file1 := &File{name: "File1"}
file2 := &File{name: "File2"}
file3 := &File{name: "File3"}
folder1 := &Folder{
name: "Folder1",
}
folder1.addNode(file1)
folder2 := &Folder{
name: "Folder2",
}
folder2.addNode(file2)
folder2.addNode(file3)
folder2.addNode(folder1)
folder2.search("rose")
}