原理
将数据组织成树状结构,统一单个对象和组合对象的处理逻辑
目的
主要用来处理树形结构的数据,如果数据满足树形结构那么使用组合模式,会使代码非常简洁
最佳实践
需求1
设计一个类来表示文件系统中的目录,能方便地实现下面这些功能:
- 动态地添加、删除某个目录下的子目录或文件;
- 统计指定目录下的文件个数;
- 统计指定目录下的文件总大小
未使用组合模式
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
}