发布于 

设计模式之组合模式

组合模式:组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,它是一种对象结构模式。

组合模式通过一种巧妙的设计方案,可以一致性地处理整个树形结构或树形结构的一部分,也可以一致性地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点(包含子节点的节点),其实就是利用对象的多态性来实现的。

组合模式结构

  • Component(抽象构件):它可以是接口或抽象类,为叶子构建和容器构建对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。
  • Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
  • Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现在抽象构件中定义的行为,包括那些访问及管理子构建的方法,在其业务方法中可以递归调用其子节点的业务方法。

例子

文件夹和文件之间的关系,非常适合用组合模式来描述。文件夹既可以包含文件,又可以包含其他文件夹,最终可能组合成一棵树。

当我用杀毒软件扫描该文件夹时,往往不会关心里面有多少文件和子文件夹,组合模式使得我们只需要操作最外层的文件夹进行扫描。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//扫描文件夹下的所有文件

//文件和文件夹的父类
abstract class Component {
name:String;
constructor(name:String){
this.name = name
}
//add往文件夹中加入文件夹或文件,由子类实现
public add(f:Component):void{}
//scan扫描文件,由子类实现
public scan():void{}
}

//文件类-叶子构件
class FileLeaf extends Component{
public add(f: Component): void {
throw new Error("不可往文件中添加文件")
}
public scan(): void {
console.log(this.name+"--正在被扫描")
}
}

//文件夹类-容器构件
class FolderComposite extends Component{
//files为存储子节点集合
files:Component[]=[];
//往里面添加文件夹或文件
public add(f: Component): void {
this.files.push(f)
}
//扫描子文件夹及文件
public scan(): void {
console.log(this.name+"-文件夹正在被扫描")
this.files.forEach(f => {
f.scan();
})
}
}

//创建一些文件夹和文件对象,并且让它们组合成一棵树
let folder = new FolderComposite("父文件夹一");
let folder1 = new FolderComposite("子文件夹1");
let folder2 = new FolderComposite("子文件夹2");

let f1 = new FileLeaf("vue的设计与实现.pdf")
let f2 = new FileLeaf("avatar.png")
let f3 = new FileLeaf("总结报告.word")

folder1.add(f1);
folder2.add(f2);
folder.add(f3);
folder.add(folder1);
folder.add(folder2);

//开始扫描folder
folder.scan();
// 父文件夹一-文件夹正在被扫描
// index.ts:19 总结报告.word--正在被扫描
// index.ts:34 子文件夹1-文件夹正在被扫描
// index.ts:19 vue的设计与实现.pdf--正在被扫描
// index.ts:34 子文件夹2-文件夹正在被扫描
// index.ts:19 avatar.png--正在被扫描