一文搞懂组合模式

原理

将数据组织成树状结构,统一单个对象和组合对象的处理逻辑

目的

主要用来处理树形结构的数据,如果数据满足树形结构那么使用组合模式,会使代码非常简洁

最佳实践

需求1

设计一个类来表示文件系统中的目录,能方便地实现下面这些功能:

  1. 动态地添加、删除某个目录下的子目录或文件;
  2. 统计指定目录下的文件个数;
  3. 统计指定目录下的文件总大小
未使用组合模式
type FileSystemNode struct {
	path     string
	isFile   bool
	subNodes []*FileSystemNode
}

func NewFileSystemNode(path string, isFile bool) *FileSystemNode {
	return &FileSystemNode{
		path:   path,
		isFile: isFile,
	}
}

func (n *FileSystemNode) CountNumOfFiles() int {
	if n.isFile {
		return 1
	}
	fileCount := 0
	for i := range n.subNodes {
		fileCount += n.subNodes[i].CountSizeOfFiles()
	}
	return fileCount
}

func (n *FileSystemNode) CountSizeOfFiles() int {
	if n.isFile {
		fileBytes, _ := os.ReadFile(n.path)
		return len(fileBytes)
	}
	fileSize := 0
	for i := range n.subNodes {
		fileSize += n.subNodes[i].CountSizeOfFiles()
	}
	return fileSize
}

func (n *FileSystemNode) AddSubNode(node *FileSystemNode) {
	n.subNodes = append(n.subNodes, node)
}

func (n *FileSystemNode) DeleteSubNode(node *FileSystemNode) {
	i := 0
	size := len(n.subNodes)
	for ; i < size; i++ {
		if n.subNodes[i].path == node.path {
			break
		}
	}
	if i < size {
		n.subNodes = append(n.subNodes[0:i], n.subNodes[i+1:]...)
	}
}

版本1从功能实现上来说没有问题,但是从扩展性(文件、目录可能会有各自不同的操作)、业务领域(文件、目录应该属于不同的业务概念)
来说并不是特别好,那么可以采用组合模式将文件当做单个对象、目录当做组合对象拆开分别统一逻辑

采用组合模式
type FileNode interface {
	CountNumOfFiles() int
	CountSizeOfFiles() int
}

type File struct {
	path string
}

func (f *File) CountNumOfFiles() int {
	return 1
}

func (f *File) CountSizeOfFiles() int {
	fileBytes, _ := os.ReadFile(f.path)
	return len(fileBytes)
}

type Dir struct {
	path     string
	subNodes []FileNode
}

func (d *Dir) CountNumOfFiles() int {
	fileCount := 0
	for i := range d.subNodes {
		fileCount += d.subNodes[i].CountSizeOfFiles()
	}
	return fileCount
}

func (d *Dir) CountSizeOfFiles() int {
	fileSize := 0
	for i := range d.subNodes {
		fileSize += d.subNodes[i].CountSizeOfFiles()
	}
	return fileSize
}

func (d *Dir) AddSubNode(node FileNode) {
	d.subNodes = append(d.subNodes, node)
}

func (d *Dir) DeleteSubNode(node FileNode) {
	i := 0
	size := len(d.subNodes)
	for ; i < size; i++ {
		if d.GetFileNodePath(d.subNodes[i]) == d.GetFileNodePath(node) {
			break
		}
	}
	if i < size {
		d.subNodes = append(d.subNodes[0:i], d.subNodes[i+1:]...)
	}
}

func (d *Dir) GetFileNodePath(node FileNode) string {
	path := ""
	switch node.(type) {
	case *File:
		path = node.(*File).path
	case *Dir:
		path = node.(*Dir).path
	}
	return path
}

采用组合模式,解耦单个对象(文件)、组合对象(目录),将单个对象、组合对象都看做树的节点,统一单个对象、组合对象的处理逻辑,
简化代码实现,达到高内聚低耦合

需求2

假设开发一个OA办公系统,人员组织结构包含部门、员工,其中部门又可以包含子部门、员工,我们希望在内存中构建出这样一个组织架构树,并且提供接口计算某个部门下所有员工的工资

组合模式代码实现
type IHuman interface {
	CalculateSalary() int
}

type Employee struct {
	salary int
}

func (e *Employee) CalculateSalary() int {
	return e.salary
}

type Department struct {
	subNodeList []IHuman
}

func (d *Department) CalculateSalary() int {
	salary := 0
	for i := range d.subNodeList {
		salary += d.subNodeList[i].CalculateSalary()
	}
	return salary
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值