设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

  1. 定义
  2. 角色
  3. 类图
  4. 种类
  5. 场景举例
  6. 实际应用场景
  7. 总结

img

定义

组合模式定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。

角色

Component(抽象构件):声明公共行为

Leaf(叶子结点,树叶构件):没有子节点

Composite(树枝构件):存储或管理子部件,有独特的list

通用类图

img

以其他形式展现

在文件系统中可改写为如图,文件为抽象类定义了对象的一些属性及方法,具体的由文件夹和文件去实现,而文件只实现操作方法(getName)

img

而在别的场景中,如组织结构,这个文件夹就相当于一个统称了,比如一级部门下有二级,二级下有三级,等等,那么最后一级部门作为叶子节点,其余部门就全部充当树枝节点了

种类

组合模式有透明方式安全方式

透明方式表示用户使用的时候不管是叶子还是树枝方法都是一样的,用户全能看到方法,但如果叶子结点调用add方法,就会出现异常

安全方式则针对于透明方式,透明方式出现异常的地方弄安全就叫安全模式,使叶子节点不实现add方法就好了

所以决定透明还是安全的就在于抽象构件这个怎么去定义

场景举例

现需要展示公司的部门层级,如何实现?

先构建类图,捋清楚之前的关系(找到了一个画图软件,比mspaint好用多了)

img

再看client如何调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//创建公司
Component company = new CompanyComposite("北京公司");
//创建部门
Component dept1 = new DeptComposite("财务部");
Component dept2 = new DeptComposite("市场部");
//创建叶子节点
Component leaf = new Leaf("最小部门");

//将部门加到公司中
company.add(dept1);
company.add(dept2);

//将最小部门加到部门中
dept1.add(leaf);
dept2.add(leaf);

//输出组织架构
company.getChild();
System.out.println("-------");
dept1.getChild();

输出截图

img

抽象接口定义(这里使用了接口,并使用了Java8的默认接口特性,子类可以选择不实现)

1
2
3
4
5
6
7
public interface Component {
String getName();
default void add(Component o){ };
default Component getChild(){
return null;
};
}

公司实现类

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
public class CompanyComposite implements Component {
private String name;
private List<Component> organizationComponents = new ArrayList<>();

public CompanyComposite(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public void add(Component o) {
organizationComponents.add(o);
}

@Override
public Component getChild() {
for (Component temp : organizationComponents) {
if (temp != null) {
System.out.println(name+"下有"+temp.getName());
temp.getChild();
}
}
return null;
}
}

部门实现类(与公司大体一致,实际情况考虑到业务不同,树枝类不写到一坨)

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
public class DeptComposite implements Component{
private String name;
private List<Component> organizationComponents = new ArrayList<>();

public DeptComposite(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}

@Override
public void add(Component o) {
organizationComponents.add(o);
}

@Override
public Component getChild() {
for (Component temp : organizationComponents) {
if (temp != null) {
System.out.println(name+"下有"+temp.getName());
temp.getChild();
}
}
return null;
}
}

叶子部门实现类

1
2
3
4
5
6
7
8
9
10
11
12
public class Leaf implements Component {
private String name;

public Leaf(String name) {
this.name = name;
}

@Override
public String getName() {
return name;
}
}

实际应用场景

①在List中定义addAll()的方法为

1
boolean addAll(Collection<? extends E> c);

这里的collection可以看作上面的Component

②JDK中AWT包和Swing包的设计是基于组合模式 ,在这些界面包中为用户提供了大量的容器构件,继承、关联于组件类Component

举个例子,比如Container类,其中的add方法

1
2
3
4
public Component add(Component comp) {
addImpl(comp, null, -1);
return comp;
}

remove方法

1
2
3
4
5
6
7
8
9
10
public void remove(Component comp) {
synchronized (getTreeLock()) {
if (comp.parent == this) {
int index = component.indexOf(comp);
if (index >= 0) {
remove(index);
}
}
}
}

总结

1、可以递归组合成任意复杂的对象

2、可随意增加新类型的Composite与Leaf的类

3、采用同一套接口操作

4、由于返回的使同一个接口类型,必要的时候需要做类型判断