9-组合模式

组合模式

image-20221205191757849

分析

  1. 在树形目录结构中,包含文件和文件夹两类不同的元素

    • 在文件夹中可以包含文件,还可以继续包含子文件夹
    • 在文件中不能再包含子文件或者子文件夹
  2. 文件夹\longleftrightarrow容器(Container)

  3. 文件\longleftrightarrow叶子(Leaf)

  4. 使用了递归调用的机制来对整个树型结构进行处理

  5. 在使用这些对象的代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下客户端希望一致地处理它们(区别对待会增加代码复杂性

树型结构:文件系统、组件、菜单等。

容器与叶子
image-20221205192258662

容器:一个(盘子)

叶子:三个(三种水果)

组合模式的定义

image-20221205192352060

对象结构型模式(有关联关系

  • 又称为==“部分-整体”(Part-Whole)模式==
  • 将对象组织到树形结构中,可以用来描述整体与部分的关系

组合模式结构

image-20221205193232372

三个关键:

  1. 抽象层:客户端面向抽象层编程,客户端可以一致地对待叶子和容器

  2. 容器与构件(Component)具有关联关系:保证了一个容器中的内容既可以是叶子也可以是容器,从而建立了多层嵌套结构。(即要与父类建立关联关系)

    • 如果关联关系建立在容器(Composite)与叶子(Leaf)之间的话就会导致容器中只能有叶子而不能再由子容器。
  3. 叶子(Leaf)拥有不必要的操作方法,破坏封装性

  • 叶子包含业务方法

  • 容器包含业务方法外还包含访问和管理成员的方法

标准的组合模式破坏了封装性

叶子Leaf类因为继承了Component从而不得不拥有add、remove、getChild等操作方法;但是叶子Leaf类只需要知道业务方法operation即可。

组合模式包含以下3个角色:
  • Component(抽象构件)

  • Leaf(叶子构件)

  • Composite(容器构件)

抽象构件角色典型代码
1
2
3
4
5
6
public 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(); //业务方法
}
叶子构件角色典型代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Leaf extends Component {
public void add(Component c) {
//异常处理或错误提示
}

public void remove(Component c) {
//异常处理或错误提示
}

public Component getChild(int i) {
//异常处理或错误提示
return null;
}

public void operation() {
//叶子构件具体业务方法的实现
}
}
容器构件角色典型代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();

public void add(Component c) {
list.add(c);
}

public void remove(Component c) {
list.remove(c);
}

public Component getChild(int i) {
return (Component)list.get(i);
}

public void operation() {
//容器构件具体业务方法的实现,将递归调用成员构件的业务方法
//如果obj是一个Composite,那么调用的operation方法将会是往下递归
//当obj是一个Leaf时,那么调用的operation方法将会是一个具体的业务实现方法
for(Object obj:list) {
((Component)obj).operation();
}
}
}
实例类图:

image-20221205194333176

image-20221205194345973

(1) AbstractFile:抽象文件类,充当抽象构件类

(2) ImageFile:图像文件类,充当叶子构件类

(3) TextFile:文本文件类,充当叶子构件类

(4) VideoFile:视频文件类,充当叶子构件类

(5) Folder:文件夹类,充当容器构件类

(6) Client:客户端测试类

结果及分析

如果需要更换操作节点,例如只对文件夹“文本文件”进行杀毒,客户端代码只需修改一行即可,例如将代码:

1
folder1.killVirus();

改为:

1
folder3.killVirus();

在具体实现时,可以创建图形化界面让用户来选择所需操作的根节点,无须修改源代码符合开闭原则

从拓展叶子上,组合模式符合开闭原则

Java AWT中的组件树
image-20221205200718610

透明组合模式

  1. 抽象构件Component中声明了所有用于管理成员对象的方法,包括add()、remove(),以及getChild()等方法

  2. 在客户端看来,叶子对象与容器对象所提供的方法是一致的,客户端可以一致地对待所有的对象

  3. 缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的

安全组合模式

  1. 抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法

  2. 对于叶子对象,客户端不可能调用到这些操作对象的方法

  3. 缺点是不够透明客户端不能完全针对抽象编程必须有区别地对待叶子构件和容器构件

image-20221205201003756

定义容器的时候只能用子类Composite来声明,客户端不能完全面向抽象编程。

模式优点

  1. 可以清楚地定义分层次的复杂对象表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制

  2. 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码

  3. 增加新的容器构件和叶子构件都很方便符合开闭原则

  4. 树形结构的面向对象实现提供了一种灵活的解决方案

模式缺点

  1. 在增加新构件时很难对容器中的构件类型进行限制

模式适用环境

  1. 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异客户端可以一致地对待它们

  2. 在一个使用面向对象语言开发的系统中需要处理一个树形结构

  3. 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型

思考题

在组合模式的结构图中,如果聚合关联关系不是从Composite到Component的,而是从Composite到Leaf,如下图所示,会产生怎样的结果?

image-20221205201545575

容器(Composite)中就只能包含叶子(Leaf),最多只能形成两层结构,不能形成多层结构。

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2024 Guijie Wang
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信