0x01.定义与类型
- 定义:将对象组合成树形结构以表示“部分-整体”的层次结构,使客户端对单个对象和组合对象保持一致的方式处理
- 组合模式实现的最关键的地方是:简单对象和复合对象必须实现相同的接口,这就是组合模式能够将组合对象和简单对象进行一致处理的原因。
- 类型:结构型
- UML类图
-
Java实现
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 1/**
2 * 组合模式统一接口类
3 */
4public interface Component {
5 void operation();
6}
7
8/**
9 * 组合类
10 */
11public class Composite implements Component {
12
13 private String name;
14
15 private List<Component> components = new ArrayList<>();
16
17 public Composite(String name) {
18 this.name = name;
19 }
20
21 public void add(Component component) {
22 components.add(component);
23 }
24
25 public void remove (Component component) {
26 components.remove(component);
27 }
28
29 public Component get(int index) {
30 return components.get(index);
31 }
32
33 @Override
34 public void operation() {
35 System.out.println(this.name);
36 for (Component component : components) {
37 component.operation();
38 }
39 }
40}
41
42/**
43 * 简单类
44 */
45public class Leaf implements Component {
46
47 private String name;
48
49 public Leaf(String name) {
50 this.name = name;
51 }
52
53 @Override
54 public void operation() {
55 System.out.println(this.name);
56 }
57
58 public String getName() {
59 return name;
60 }
61}
62
63
-
测试与应用类
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 1/**
2 * 应用与测试
3 */
4public class Test {
5
6 public static void main(String[] args) {
7 Composite composite = new Composite("树枝1");
8
9 Leaf leaf1 = new Leaf("树枝1树叶1");
10 Leaf leaf2 = new Leaf("树枝1树叶2");
11 Leaf leaf3 = new Leaf("树枝1树叶3");
12
13 composite.add(leaf1);
14 composite.add(leaf2);
15 composite.add(leaf3);
16
17 Composite composite1 = new Composite("树");
18
19 Leaf leaf4 = new Leaf("树叶4");
20 Leaf leaf5 = new Leaf("树叶5");
21
22 composite1.add(leaf4);
23 composite1.add(leaf5);
24 composite1.add(composite);
25
26 composite1.operation();
27 }
28}
29
30
-
输入结果
1
2
3
4
5
6
7
8
9 1树
2树叶4
3树叶5
4树枝1
5树枝1树叶1
6树枝1树叶2
7树枝1树叶3
8
9
-
组合模式中的透明式以及安全式
-
透明式:组合模式中的抽象构件还声明访问和管理子类的接口,客户端使用的适合不需要区分叶子节点和树枝节点,但是叶子节点本身并不存在操作方法,一般会给出默认实现,比如抛出异常。
- 安全式:组合模式中的抽象构件不声明管理子类的接口,把操作移交给子类完成。这样叶子节点不需要实现操作方法,但是客户端使用时必须做出区分,为使用带来麻烦。
- 上面的方式中使用的是安全模式。
-
组合模式中的角色
-
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件声明的公共接口。
- 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 add()、remove()、get() 等方法。
0x02.适用场景
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象时。
- 当想表达对象的部分-整体的层次结构时。
0x03.优点
- 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
- 组合模式使得客户端代码可以一致地处理对象和对象容器,无需关心处理的单个对象,还是组合的对象容器。
- 简化客户端代码
- 符合开闭原则
0x04.缺点
- 限制类型时会较为复杂。
- 使设计变得更加抽象,客户端需要花更多时间理清类之间的层次关系。
0x05.组合模式实现样例
使用组合模式实现目录和课程之间的关系。
-
因为上面给出的basic实现安全模式的,这次的样例使用透明模式实现。
-
Java代码
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 1/**
2 * 通用的抽象类
3 */
4public abstract class CatalogComponent {
5
6 public void add (CatalogComponent catalogComponent) {
7 throw new UnsupportedOperationException("不支持添加操作");
8 }
9
10 public void remove (CatalogComponent catalogComponent) {
11 throw new UnsupportedOperationException("不支持删除操作");
12 }
13
14 public String getName () {
15 throw new UnsupportedOperationException("不支持获取名称操作");
16 }
17
18 public Double getPrice () {
19 throw new UnsupportedOperationException("不支持获取价钱操作");
20 }
21
22 public void print () {
23 throw new UnsupportedOperationException("不支持打印操作");
24 }
25
26}
27
28/**
29 * 目录
30 */
31public class CourseCatalog extends CatalogComponent {
32
33 private List<CatalogComponent> itsms = new ArrayList<>();
34
35 private String name;
36
37 private Integer level;
38
39 public CourseCatalog(String name, Integer level) {
40 this.name = name;
41 this.level = level;
42 }
43
44 @Override
45 public String getName() {
46 return this.name;
47 }
48
49 @Override
50 public void add(CatalogComponent catalogComponent) {
51 this.itsms.add(catalogComponent);
52 }
53
54 @Override
55 public void remove(CatalogComponent catalogComponent) {
56 this.itsms.remove(catalogComponent);
57 }
58
59 @Override
60 public void print() {
61 System.out.println("> " + this.name);
62 for (CatalogComponent catalogComponent : itsms) {
63 if (this.level != null) {
64 for (int i = 0; i < this.level; i ++) {
65 System.out.print("--");
66 }
67 }
68 catalogComponent.print();
69 }
70 }
71}
72
73/**
74 * 具体的课程
75 */
76public class Course extends CatalogComponent {
77
78 private String name;
79
80 private Double price;
81
82 public Course(String name, Double price) {
83 this.name = name;
84 this.price = price;
85 }
86
87 @Override
88 public String getName() {
89 return this.name;
90 }
91
92 @Override
93 public Double getPrice() {
94 return this.price;
95 }
96
97 @Override
98 public void print() {
99 System.out.println("> Course Name:" + this.name + ": price: " + this.price);
100 }
101}
102
103
-
测试与应用
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 1/**
2 * 测试与应用
3 */
4public class Test {
5
6 public static void main(String[] args) {
7 CatalogComponent linuxCourse = new Course("Linux课程", 11D);
8 CatalogComponent windowCourse = new Course("Window课程", 12D);
9
10 CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录", 2);
11
12 CatalogComponent mmallCourse1 = new Course("Java电商一期", 55D);
13 CatalogComponent mmallCourse2 = new Course("Java电商二期", 66D);
14 CatalogComponent designPattern = new Course("Java设计模式", 77D);
15
16 javaCourseCatalog.add(mmallCourse1);
17 javaCourseCatalog.add(mmallCourse2);
18 javaCourseCatalog.add(designPattern);
19
20 CatalogComponent mainCourseCatalog = new CourseCatalog("课程主目录", 1);
21
22 mainCourseCatalog.add(linuxCourse);
23 mainCourseCatalog.add(windowCourse);
24 mainCourseCatalog.add(javaCourseCatalog);
25
26 mainCourseCatalog.print();
27 }
28}
29
30
31
- UML类图
0x06.相关设计模式
-
组合模式和访问者模式
-
可以适用访问者模式来访问组合模式中的递归结构
0x07.源码中的组合模式
- java.awt.Container
- HashMap.putAll
- ArrayList.addAll
- MyBatis.SqlNode
0x08.代码地址
0x09.参考
- 慕课网设计模式精讲: https://coding.imooc.com/class/270.html
- 组合模式(详解版): http://c.biancheng.net/view/1373.html
- 设计模式之组合模式: https://www.cnblogs.com/snaildev/p/7647190.html