组合模式针对于特定场景,如文件管理、组织管理等,使用该模式能简化管理,使代码变得非常简洁。
UML类图位置:https://www.processon.com/diagraming/609b375407912943913a4c13
本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/16composite.go
1.定义
1.1组合模式
组合模式:将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
UML:
1.2分析
单看UML图可能不清晰,举个栗子会容易一些。Composite是目录,Leaf是目录下的文件,目录和文件都继承自Component。目录能够增加、删除文件,可以展示目录所在位置,文件只能展示文件所在位置。
对于目录这种需求,有两种实现方式。
第一种不使用组合模式,只用一个类,有2个核心变量
- 一个成员变量表明对象时文件还是目录
- 一个成员变量存放目录下文件列表,如果对象为文件,则该变量为空
type FileSystemNode struct {
isFile bool //表明是文件还是目录
subNodes []FileSystemNode //目录下包含的内容
}
第二种方案使用组合模式。虽然第一种方案能够实现文件管理的功能,但并不优雅。因为文件和目录是不同的,各自有各自的特性,将特有的内容放到一个类里,不满足单一职责原则。
所以我们可以将其拆分为两个类:文件类和目录类。两个类必须继承自同一个父类,除了重复的功能可以复用外,更重要的一点是消除了两个类调用上的区别,subNodes不需要做任何区分。而且这两个类可以独立进化,相互不影响,何乐而不为呢。
2.使用场景
组合模式在使用上,特别像深度优先遍历或者广度优先遍历,一般用于组织结构、文件管理上,这些功能都有共通点:个体和集体无论在功能上还是认知上都极为相似。
字节跳动的协同办公软件-飞书,在文档管理部分,就极其适合使用组合模式,大家有时间可以尝试一下,链接如下:https://www.feishu.cn/ ,其展现样式如下所示:
3.代码实现
这次代码简单实现一下目录和文件的添加、显示功能吧。
package main
import "fmt"
const Separator = "--"
/**
* @Author: Jason Pang
* @Description: 文件系统接口,文件和目录都要实现该接口
*/
type FileSystemNode interface {
Display(separator string)
}
/**
* @Author: Jason Pang
* @Description: 文件通用功能
*/
type FileCommonFunc struct {
fileName string
}
/**
* @Author: Jason Pang
* @Description: 设置文件名称
* @receiver f
* @param fileName
*/
func (f *FileCommonFunc) SetFileName(fileName string) {
f.fileName = fileName
}
/**
* @Author: Jason Pang
* @Description: 文件类
*/
type FileNode struct {
FileCommonFunc
}
/**
* @Author: Jason Pang
* @Description: 文件类显示文件内容
* @receiver f
*/
func (f *FileNode) Display(separator string) {
fmt.Println(separator + f.fileName + " 文件内容为:Hello,world")
}
/**
* @Author: Jason Pang
* @Description: 目录类
*/
type DirectoryNode struct {
FileCommonFunc
nodes []FileSystemNode
}
/**
* @Author: Jason Pang
* @Description: 目录类展示文件名
* @receiver d
*/
func (d *DirectoryNode) Display(separator string) {
fmt.Println(separator + d.fileName)
for _, node := range d.nodes {
node.Display(separator + Separator)
}
}
/**
* @Author: Jason Pang
* @Description: 添加目录或者文件
* @receiver d
* @param f
*/
func (d *DirectoryNode) Add(f FileSystemNode) {
d.nodes = append(d.nodes, f)
}
func main() {
//初始化
biji := DirectoryNode{}
biji.SetFileName("笔记")
huiyi := DirectoryNode{}
huiyi.SetFileName("会议")
chenhui := FileNode{}
chenhui.SetFileName("晨会.md")
zhouhui := FileNode{}
zhouhui.SetFileName("周会.md")
//组装
biji.Add(&huiyi)
huiyi.Add(&chenhui)
huiyi.Add(&zhouhui)
//显示
biji.Display(Separator)
}
显示:
➜ myproject go run main.go
–笔记
----会议
------晨会.md 文件内容为:Hello,world
------周会.md 文件内容为:Hello,world
文件类和目录类都实现了FileSystemNode接口,所以目录类管理文件类如同管理自己一样。两者都组合了FileCommonFunc类,可以复用相同功能。最后就是两者可以独立变化,如目录类有Add功能,但文件类没有。
3.总结
组合模式是对指定场景有用,所以大家能不能用到,完全看运气。这个设计模式满足单一职责原则、开闭原则、里氏替换原则。
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾:
招聘
- 字节跳动|内推大放送
- 字节跳动|今日头条广州服务端研发工程师内推
- 字节跳动|抖音电商急招上海前端开发工程
- 字节跳动|抖音电商上海资深服务端开发工程师-交易
- 字节跳动|抖音电商武汉服务端(高级)开发工程师
- 字节跳动|飞书大客户产品经理内推咯
- 字节跳动|抖音电商服务端技术岗位虚位以待
- 字节跳动招聘专题
设计模式
- Go设计模式(15)-门面模式
- Go设计模式(14)-适配器模式
- Go设计模式(13)-装饰器模式
- Go设计模式(12)-桥接模式
- Go设计模式(11)-代理模式
- Go设计模式(10)-原型模式
- Go设计模式(9)-建造者模式
- Go设计模式(8)-抽象工厂
- Go设计模式(7)-工厂模式
- Go设计模式(6)-单例模式
- Go设计模式(5)-类图符号表示法
- Go设计模式(4)-代码编写优化
- Go设计模式(4)-代码编写
- Go设计模式(3)-设计原则
- Go设计模式(2)-面向对象分析与设计
- Go设计模式(1)-语法
语言
- 再也不怕获取不到Gin请求数据了
- 一文搞懂pprof
- Go工具之generate
- Go单例实现方案
- Go通道实现原理
- Go定时器实现原理
- Beego框架使用
- Golang源码BUG追查
- Gin框架简洁版
- Gin源码剖析
架构
存储
网络
工具
读书笔记
思考