组合模式

组合模式

一、定义

摘自百度百科: 组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。

点击查看源码


二、角色分类

抽象构件(Component)

声明了参与组合对象的共有方法和属性,定义了访问其子构件的方法

叶子构件(Leaf)

其表示叶子节点,叶子节点没有子节点,是遍历的最小单位,它实现了在抽象构件中定义的一些行为,

树枝构件(Composite)

它的作用是组合树枝节点和叶子节点构成一个树枝结构,它提供了一个集合来存储子节点,并且实现了抽象构件中定义的行为,包括访问和管理子构件的方法

客户角色(Client)

调用方法的角色


三、实现方式

UML图

未命名文件

具体实现

透明模式

抽象构件(Component)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Component {
// 个体与整体都有
public void operation() {
// 业务逻辑
}
// 增加一个叶子构件或树枝构件
public abstract void add(Component component);

// 删除一个叶子构件或树枝构件
public abstract void remove(Component component);

// 获取分支下的所有叶子构件和树枝构件
public abstract List<Component> getChildren();
}

树枝构件(Composite)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Composite extends Component {
// 构件容器
private List<Component> componentList = new ArrayList<>();

// 增加一个叶子构件或树枝构件
public void add(Component component) {
this.componentList.add(component);
}

// 删除一个叶子构件或树枝构件
public void remove(Component component) {
this.componentList.remove(component);
}

// 获取分支下所有叶子构件和树枝构件
public List<Component> getChildren() {
return this.componentList;
}
}

叶子构件(Leaf)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Leaf extends Component {
public void add(Component component) {
// 空实现
}

public void remove(Component component) {
// 空实现
}

public List<Component> getChildren() {
// 空实现
}
}

客户角色(Client)

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
public class Client {
public static void main(String[] args) {
// 创建一个根节点
Composite root = new Composite();
root.operation();
// 创建一个树枝构件
Composite branch = new Composite();
// 创建一个叶子节点
Leaf leaf = new Leaf();
// 建立整体
root.add(branch);
branch.add(leaf);
}

public static void showTree(Component root) {
root.getChildren.foreach(any->{
if(any instanceof Leaf) {
// 叶子构件
any.operation();
}else {
// 树枝构件
showTree(any);
}
});
}
}

安全模式

抽象构件(Component)

1
2
3
4
5
6
7
public abstract class Component {
// 个体和整体都有
public void operation() {
// 业务逻辑
System.out.println("Component: operation被执行了");
}
}

树枝构件(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
public class Composite extends Component {

// 构件容器
private List<Component> componentList = new ArrayList<>();

/**
* 新增一个叶子构件或树枝构件
* @param component
*/
public void add(Component component) {
this.componentList.add(component);
}

/**
* 删除一个叶子构件或树枝构件
*/
public void remove(Component component) {
this.componentList.remove(component);
}

/**
* 获取分支下的所有叶子构件和树枝构件
*/
public List<Component> getChildren() {
return this.componentList;
}

}

叶子构件(Leaf)

1
2
3
4
5
6
7
public class Leaf extends Component {

public void operation() {
System.out.println("Leaf: operation被执行了");
}

}

客户角色(Client)

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
public class Client {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.operation();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
showTree(root);
}

//通过递归遍历树
public static void showTree(Composite root){
for(Component c:root.getChildren()){
if(c instanceof Leaf){
// 叶子节点
c.operation();
}else{
// 树枝节点
showTree((Composite)c);
}
}
}
}

透明模式和安全模式的区别

  • 安全模式在抽象组件中只定义一些默认的行为或属性,它是把树枝节点和树叶节点彻底分开;透明模式是把用来组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,通过判断确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题,不是很建议的方式。
  • 安全模式与依赖倒置原则冲突;透明模式的好处就是它基本遵循了依赖倒转原则,方便系统进行扩展。
  • 安全模式在遍历树形结构的的时候需要进行强制类型转换;在透明模式下,遍历整个树形结构是比较容易的,不用进行强制类型转换。

四、应用场景

以下部分内容摘自 菜鸟教程

意图: 将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

主要解决: 它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

何时使用:

  1. 您想表示对象的部分-整体层次结构(树形结构)。
  2. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

如何解决: 树枝和叶子实现统一接口,树枝内部组合该接口。

关键代码: 树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

应用实例:

  1. 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。
  2. 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。

使用场景: 部分、整体场景,如树形菜单,文件、文件夹的管理。

注意事项: 定义时为具体类。


五、优缺点

优点

  1. 高层模块调用简单。
  2. 节点自由增加。

缺点

在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。