概念
组合模式(Composite Pattern),又叫 “部分整体” 模式,将对象组合成树形结构,以表示 “部分-整体” 的层次结构。通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性.
UML图
具体实现
有3种角色对象,
- 根节点
- 树枝节点(组合节点), 可以向下包含树枝节点和叶子结点
- 叶子节点, 最底层的节点
代码实现
- 定义一个所有节点统一的Component操作接口, 方法包含添加节点,遍历节点, 节点名称设置,获取。
- 定义一个节点生成工厂结构体, 成员变量为节点名称,实现Component接口, 通过传入不同的节点字段,得到不同的节点实例。
- 定义一个树枝节点, 节点继承工厂结构体, 通过嵌套匿名工厂结构体的方式, 内部成员Component 切片, 覆盖添加节点和遍历节点方法
- 定义一个叶子节点, 节点继承工厂结构体, 覆盖打印名称节点。
模式的场景和优缺点
使用场景
组合模式应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方:
「命令分发:」 只需要通过请求树的最顶层对象,便能对整棵树做统一的操作。在组合模式中增加和删除树的节点非常方便,并且符合开放-封闭原则;
「统一处理:」 统一对待树中的所有对象,忽略组合对象和叶对象的区别
特点
- 表示 “部分-整体” 的层次结构,生成 "树叶型" 结构
- 操作一致操作性,所有节点操作具有一致性。
- 自上而下的的请求流向,从树对象传递给叶对象
- 调用顶层对象,会自行遍历其下的叶对象执行
- 组合模式的遍历, 本质上是一种递归的代码结构
优点
- 高层模块调用简单
- 节点自由增加
缺点
- .
代码实现
package main
import (
"fmt"
)
// Component 所有节点的操作接口
type Component interface {
GetName() string
SetName(string)
AddChild(Component)
Print(string)
}
const (
// LeafNode ...
LeafNode = iota
// CompositeNode ...
CompositeNode
)
// NewComponent 生成节点, 节点生成工厂
func NewComponent(kind int, name string) Component {
var c Component
switch kind {
case LeafNode:
// 叶子
c = NewLeaf()
case CompositeNode:
// 根
c = NewComposite()
}
c.SetName(name)
return c
}
// component ...它实现了component接口,树枝节点(组合节点)和叶子节点通过嵌套匿名匿名结构体的方式实现了继承
// 除了叶子结点和树枝节点特有的功能需要单独实现。
type component struct {
name string
}
// Name ...
func (c *component) GetName() string {
return c.name
}
// SetName ...
func (c *component) SetName(name string) {
c.name = name
}
// AddChild 根才能添加孩子节点,根会实现
func (c *component) AddChild(Component) {}
func (c *component) Print(string) {}
// Leaf ...
type Leaf struct {
component
}
// NewLeaf ...
func NewLeaf() *Leaf {
return &Leaf{}
}
// Print ...
func (c *Leaf) Print(pre string) {
fmt.Printf("%s-%s\n", pre, c.GetName())
}
// Composite ...
type Composite struct {
component
childs []Component
}
// NewComposite ...
func NewComposite() *Composite {
return &Composite{
childs: make([]Component, 0),
}
}
// AddChild 根才能添加孩子节点
func (c *Composite) AddChild(child Component) {
c.childs = append(c.childs, child)
}
// Print ...
func (c *Composite) Print(pre string) {
fmt.Printf("%s+%s\n", pre, c.GetName())
pre += " "
for _, comp := range c.childs {
comp.Print(pre)
}
}
func main() {
root := NewComponent(CompositeNode, "root")
c1 := NewComponent(CompositeNode, "c1")
c2 := NewComponent(CompositeNode, "c2")
c3 := NewComponent(CompositeNode, "c3")
l1 := NewComponent(LeafNode, "l1")
l2 := NewComponent(LeafNode, "l2")
l3 := NewComponent(LeafNode, "l3")
// 树状结构
root.AddChild(c1)
root.AddChild(c2)
c1.AddChild(c3)
c1.AddChild(l1)
c2.AddChild(l2)
c2.AddChild(l3)
root.Print("")
}
// root.Print("")
// 输入结果如下, 第一个root 非 child, 直接打印。 后面的打印顺序和添加顺序不同, +c1 +c3 -l1 ,调用到c1 child的时候,
// 又是一个新的c1的child列表,Print会调用到c1的子 child, 这里的存在一个Print的递归调用。
/*
+root
+c1
+c3
-l1
+c2
-l2
-l3
*/