# 组合模式
# 参考
http://c.biancheng.net/view/1373.html (opens new window)
# 定义
有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性, 他们能够合作的关键点是拥有相同的接口。
这里要注意:组合模式不是父子关系,而是聚合关系。看到了组合模式的树形结构很容易让人误会组合对象和简单对象是父子关系,这并不正确。 组合模式是一种HAS-A(聚合)的关系,而不是IS-A。
组合对象包含一组叶子对象,但叶子对象并不一定是组合对象的子类。比如公司跟部门,公司包含了很多部门,但是部门并不一定拥有公司的各种职能。
# 应用场景
UI组件中tree组件,文件系统中的文件、文件夹,公司以及公司中的部门等简单对象与符合对象的处理。
# 优点
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码。
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”。
# 缺点
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系。
- 不容易限制容器中的构件。
- 不容易用继承的方法来增加构件的新功能。
# 代码实例
// 模拟文件系统
class BasicFile {
name: string
parent: SysFolder
constructor(name: string) {
this.name = name
}
isSameFile(file1, file2): boolean {
return file1 === file2
}
remove() {
if (!this.parent) return
this.parent.files.splice(this.parent.files.findIndex(f => this.isSameFile(f, this)), 1)
}
}
class SysFolder extends BasicFile {
files: Array<SysFolder | SysFile> = []
add(file: SysFolder | SysFile): void {
file.parent = this
this.files.push(file)
}
scan() {
console.log(`Start scanning folder: ${this.name}`)
this.files.forEach(f => f.scan())
}
}
class SysFile extends BasicFile {
scan() {
console.log(`Start scanning file: ${this.name}`)
}
}
const folder1 = new SysFolder('文件夹1')
const folder2 = new SysFolder('文件夹2')
const folder3 = new SysFolder('文件夹3')
const file1 = new SysFile('文件1')
const file2 = new SysFile('文件2')
const file3 = new SysFile('文件3')
const file4 = new SysFile('文件4')
folder1.add(file1)
folder1.add(file2)
folder1.add(folder2)
folder1.add(folder3)
folder2.add(file3)
folder2.add(file4)
folder3.remove()
file3.remove()
folder1.scan()