DesignPatternCompisite组合模式
DesignPatternCompisite组合模式
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
官方代码1
package composite
import "fmt"
type Component interface {
Parent() Component
SetParent(Component)
Name() string
SetName(string)
AddChild(Component)
Print(string)
}
const (
LeafNode = iota
CompositeNode
)
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
}
type component struct {
parent Component
name string
}
func (c *component) Parent() Component {
return c.parent
}
func (c *component) SetParent(parent Component) {
c.parent = parent
}
func (c *component) Name() string {
return c.name
}
func (c *component) SetName(name string) {
c.name = name
}
func (c *component) AddChild(Component) {}
func (c *component) Print(string) {}
type Leaf struct {
component
}
func NewLeaf() *Leaf {
return &Leaf{}
}
func (c *Leaf) Print(pre string) {
fmt.Printf("%s-%s\n", pre, c.Name())
}
type Composite struct {
component
childs []Component
}
func NewComposite() *Composite {
return &Composite{
childs: make([]Component, 0),
}
}
func (c *Composite) AddChild(child Component) {
child.SetParent(c)
c.childs = append(c.childs, child)
}
func (c *Composite) Print(pre string) {
fmt.Printf("%s+%s\n", pre, c.Name())
pre += " "
for _, comp := range c.childs {
comp.Print(pre)
}
}
func Tm() {
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("")
}
# 测试文件
package component
import "testing"
func TestTTmain(t *testing.T) {
TTTmain()
}
#输出
+root
+c1
+c3
-l1
+c2
-l2
-l3
官方代码2(来自B站蜡笔小程序员)
他的这个我不太喜欢
package Composite
import "fmt"
type Component interface {
Traverse()
}
type Leaf struct {
value int
}
func NewLeaf(value int) *Leaf {
return &Leaf{value}
}
func (l *Leaf) Traverse() {
fmt.Println(l.value)
}
type Composite struct {
children []Component
}
func NewComposite() *Composite {
return &Composite{children: make([]Component, 0)}
}
func (c *Composite) Add(component Component) {
c.children = append(c.children, component)
}
func (c *Composite) Traverse() {
for idx, _ := range c.children {
c.children[idx].Traverse()
}
}
# 测试代码
package Composite
import (
"fmt"
"testing"
)
func TestComposite_Traverse(t *testing.T) {
containers := make([]Composite, 4)
for i := 0; i < 4; i++ {
for j := 0; j < 3; j++ {
containers[i].Add(NewLeaf(i*3 + j))
}
}
for i := 1; i < 4; i++ {
containers[0].Add(&(containers[i]))
}
for i := 0; i < 4; i++ {
containers[i].Traverse()
fmt.Printf("\n")
}
}
毛哥实际业务场景模拟
- 模拟实现文件层级结构
- 分析可知,文件系统只有文件夹可以是父亲节点
- 这在设置父亲节时候,可以很方便
- 对应官方的就是文件夹 -> Composite, 文件 -> Leaf
package component
import "fmt"
// 文件系统展开
// 文件夹是继续展开
// 文件是输出内容
type IFiler interface {
// 为孩子设置父亲节点为自己
SetParent(fa IFiler)
// 获取父亲节点的名字
GetParentName() string
// 打印文件包括文件夹
Fopen()
}
// 文件夹
type Floder struct {
// 文件夹名称
Name string
Parent IFiler
// 文件夹的孩子可以是文件或者文件夹,所以用接口替代
Childs []IFiler
}
func NewFloder(n string) *Floder {
return &Floder{
Name: n,
Childs: make([]IFiler, 0),
}
}
// 如果是文件就继续打开,如果是文件就输出里面内容
func (f *Floder) Fopen() {
for _, v := range f.Childs {
fmt.Println(f.Name,"父亲节点是", f.GetParentName())
v.Fopen()
}
}
func (f *Floder) AddChilds(fi IFiler) {
f.SetParent(fi)
f.Childs = append(f.Childs, fi)
}
// son设置父亲节点为f
func (f *Floder) SetParent(son IFiler) {
switch son.(type) {
case *Floder:
floder := son.(*Floder)
floder.Parent = f
case *FileReal:
fileReal := son.(*FileReal)
fileReal.Parent = f
}
}
func (f *Floder) GetParentName() string {
floder := f.Parent.(*Floder)
return floder.Name
}
type FileReal struct {
Name string
Parent IFiler
Value int // 模拟文件里面的数据
}
func NewFildReal(n string, v int) *FileReal {
return &FileReal{
Name: n,
Value: v,
}
}
// 因为是文件,打不开了所以直接输出内容
func (f *FileReal) Fopen() {
fmt.Println(f.Name, f.Value, "---父亲节点是:", f.GetParentName())
}
func (f *FileReal) SetParent(fa IFiler) {
}
func (f *FileReal) GetParentName() string {
floder := f.Parent.(*Floder)
return floder.Name
}
func TTTmain() {
nul := NewFloder("nul")
root := NewFloder("root")
one := NewFloder("one")
two := NewFloder("two")
h1 := NewFloder("h1")
h2 := NewFloder("h2")
f1 := NewFildReal("f1", 10)
f2 := NewFildReal("f2", 20)
nul.AddChilds(root)
root.AddChilds(one)
root.AddChilds(two)
one.AddChilds(h1)
two.AddChilds(h2)
h1.AddChilds(f1)
h2.AddChilds(f2)
one.Fopen()
}
# 根据最后的tttmain()输出
one 父亲节点是 root
h1 父亲节点是 one
f1 10 ---父亲节点是: h1
总结
这些模式和那个数据结构树很相似,这里面用的是切片,树用的是链表