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

总结

这些模式和那个数据结构树很相似,这里面用的是切片,树用的是链表

posted @ 2020-12-24 15:52  maob  阅读(95)  评论(0编辑  收藏  举报