组合模式

将对象组合成树形结构以表示‘部分-整体’的曾侧结构。并利用多态性使单个对象和组合对象的使用具有一致性。

概述

  • 通过对命令的封装,可一键对各种命令进行执行,不用关心各命令的执行过程,并依次向下传递
  • 用户只需关心最顶层的组合对象即可
  • 可以一致的对待组合对象和基本对象

简例

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
var macroCommand = function (){
var commandList = []
return {
add (command){
this.commandList.push(command)
},
excute(){
commandList.forEach(command => command.excute())
}
}
}
var command 1 = {
excute() {
console.log(‘命令1’)
}
}
var command 2 = {
excute() {
console.log(‘命令2’)
}
}
var command 3 = {
excute() {
console.log(‘命令3’)
}
}
var command 4 = {
excute() {
console.log(‘命令4’)
}
}
var macroCommand1 = macroCommand()
macroCommand1.add(command1)
macroCommand1.add(command2)
var macroCommand2 = macroCommand()
macroCommand2.add(command3)
macroCommand2.add(command4)
var macroCommand3 = macroCommand()
macroCommand3.add(macroCommand1)
macroCommand3.add(macroCommand2)
macroCommand3.excute()

可见,我们的组合命令可以任意的向下扩展,仅仅关心执行即可。

透明性带来的安全问题

  • 组合对象都带有add方法而叶节点没有
  • 通过给叶节点添加同样的方法并抛出异常来解决

注意

1.组合模式不是父子关系

组合对象是一种聚合关系,叶节点并不是组合对象的子类,只是具有相同的接口

2.针对叶节点操作的一致性

针对一系列叶节点,需要有一致性操作,假设有特殊情况,就不太方便了。

3.双向映射关系

对于某种情况,假设是给员工发邮件,假设员工隶属多个组,会重复收到。这种结构是不合适的,往往需要建立双向映射关系。
我们可以通过中介者模式来管理这些对象。

4.用职责链模式提高组合模式性能

借用职责模式避免遍历整棵树

引用父对象

有的时候还是需要保持父级组合对象的引用,来进行更多的操作,可以通过这种方式实现

1
2
3
4
5
6
7
8
9
10
11
12
13
var macroCommand = function (){
return {
parent: null,
commandList = [],
add (command){
this.commandList.push(command)
command.parent = this
},
excute(){
this.commandList.forEach(command => command.excute())
}
}
}

总结

组合模式可以大大简化用户代码,适用于以下两种情况:

  • 表示对象的部分-整体结构,进行统一操作。在组合模式中添加和删除节点非常方便。
  • 客户需要统一对待树中所有的对象,用户不用关心正在处理的是组合对象还是叶对象。