设计模型|组合模式

Yeren Lv3

定义

组合模式(Composite Pattern):将对象组合成树形结构以表示‘部分-整体’的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

动机

组合模式是一个比较特化的设计模式,一般不常用,但是一旦用到了就说明这个问题不用组合模式将会十分困难。一般来说,它有以下的应用场景:

  • 当你的程序结构有类似树一样的层级关系时,例如文件系统,视图树,公司组织架构等等。
  • 当你要以统一的方式操作单个对象和由这些对象组成的组合对象的时候。

设计类图

结构

  • Component:组合中的对象声明接口,在适当情况下实现所有类共有的默认行为,声明一个接口用于访问和管理Component的子组件。在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
  • Leaf:在组合中表示叶节点,叶节点没有子节点,定义对象的基本行为。
  • Composite:定义有子部件的那些部件的行为,存储子部件并在Component接口实现与子部件有关的操作。
  • Client:通过Component接口操作组合部件的对象。

样例代码

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
package DesignPattern;

import java.util.ArrayList;
import java.util.Arrays;

abstract class Component{
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
public abstract void operation();
}

class Leaf extends Component{

@Override
public void add(Component c) {
System.out.println("leaf can not add component");
}

@Override
public void remove(Component c) {
System.out.println("leaf can not remove component");
}

@Override
public Component getChild(int i) {
System.out.println("leaf can not get child");
return null;
}

@Override
public void operation() {
// operation ...
System.out.println("do some operations");
// operation ...
}
}

class Composite extends Component{
private ArrayList<Component> children = new ArrayList<>();

@Override
public void add(Component c) {
children.add(c);
}

@Override
public void remove(Component c) {
children.remove(c);
}

@Override
public Component getChild(int i) {
return children.get(i);
}

@Override
public void operation() {
// operation ...
System.out.println("this is a composite");
for(Component c:children){
c.operation();
}
}
}

class main{
public static void main(String[] args) {
Component root = new Composite();
for(int i = 0; i < 5; i ++){
Component c = new Leaf();
root.add(c);
}
Component second = new Composite();
for(int i = 0; i < 3; i ++){
Component c = new Leaf();
second.add(c);
}
root.add(second);
root.operation();
}
}

补充

组合模式的实现根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(诸如:add、remove等)。安全式的组合模式要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件中。与安全式的组合模式不同的是,透明式的组合模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定的接口。

 透明模式:也就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具有完全一致的行为接口,但问题也很明显,因为Leaf类本身不具备Add(),Remove()方法的功能,所以实现他是没有意义的。

安全模式:就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样就不会出现透明模式出现的问题,不过由于不够透明,所以叶节点和枝节点将不具有相同的接口,客户端调用需要做相应的判断,带来了不便。

总结分析

  • 通用操作定义在Component中,根据使用方式不同,透明方式与安全方式,有一定的不同
  • 组合节点Composite不仅要继承Component,而且要持有一个Component的集合
  • 叶子对象只继承Component即可

优点

  • 可以递归组合成任意复杂的对象
  • 可随意增加新类型的CompositeLeaf的类
  • 简化了客户端代码,因为不论对象多么复杂客户端都是以同一套接口操作

缺点

  • 如果需要确定某个组件是特殊组织,然后针对它做特殊的操作,就需要在运行时判断。

例题

假设你在设计你们公司的组织架构系统,其中公司的所有人都隶属于公司部门,公司部门包括IT部门、人力部门、市场部门以及总裁、副总裁,IT部门包括部长、前端部门、后端部门,市场部门包括部长、销售部门、调研部门。每个子部门下均有若干部员。

请你设计一个系统,可以灵活的增删改查公司员工。

参考代码

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

package DesignPattern;

import java.util.ArrayList;

abstract class Component{
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
public abstract void operation();
}


class Leaf extends Component{

public int id;
public Leaf(int id_){
id = id_;
}

@Override
public void add(Component c) {
System.out.println("leaf can not add component");
}

@Override
public void remove(Component c) {
System.out.println("leaf can not remove component");
}

@Override
public Component getChild(int i) {
System.out.println("leaf can not get child");
return null;
}

@Override
public void operation() {
// operation ...
System.out.println("this employee's id is " + id);
// operation ...
}
}

class Composite extends Component{
private ArrayList<Component> children = new ArrayList<>();
public String departmentName;

public Composite(String name){
departmentName = name;
}

@Override
public void add(Component c) {
children.add(c);
}

@Override
public void remove(Component c) {
children.remove(c);
}

@Override
public Component getChild(int i) {
return children.get(i);
}

@Override
public void operation() {
// operation ...
System.out.println("this is " + departmentName);
for(Component c:children){
c.operation();
}
}
}

class main{
public static void main(String[] args) {
Component root = new Composite("Company");
Component boss = new Leaf(1);
Component viceBoss = new Leaf(2);
Component IT = new Composite("IT Department");
Component HR = new Composite("HR Department");
Component Market = new Composite("Marketing Department");
root.add(boss);
root.add(viceBoss);
root.add(IT);
root.add(HR);
root.add(Market);

Component ITMinister = new Leaf(3);
Component frontEnd = new Composite("Front End Department");
Component backEnd = new Composite("Back End Department");
for(int i = 4; i < 7; i ++){
Component c = new Leaf(i);
frontEnd.add(c);
}
for(int i = 7; i < 10; i ++){
Component c = new Leaf(i);
backEnd.add(c);
}
IT.add(ITMinister);
IT.add(frontEnd);
IT.add(backEnd);

for(int i = 10; i < 16; i ++){
Component c = new Leaf(i);
HR.add(c);
}

Component MarketMinister = new Leaf(16);
Component sale = new Composite("Sales Department");
Component survey = new Composite("Survey Department");
for(int i = 17; i < 20; i ++){
Component c = new Leaf(i);
sale.add(c);
}
for(int i = 20; i < 26; i ++){
Component c = new Leaf(i);
survey.add(c);
}
Market.add(MarketMinister);
Market.add(sale);
Market.add(survey);
root.operation();
}
}

  • Title: 设计模型|组合模式
  • Author: Yeren
  • Created at : 2023-08-22 00:00:00
  • Updated at : 2023-08-02 00:00:00
  • Link: https://blog.yeren.xyz/2023/08/22/DP-composite-pattern/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments