设计模式之组合模式详解(Java实现)
1 组合模式介绍
在我们的树形目录结构中,包含文件和文件夹两类不同的元素,如下图。
其中文件夹中可以包含文件,也可以继续包含文件夹;而在文件中不能再包含子文件或者子文件夹。
那么我们可以将文件夹看作是容器(Container),将文件看作是叶子(Leaf)。那如何一致的对待容器对象和叶子对象呢?
组合模式(Composite Pattern)让客户端可以统一对待单个对象和组合对象。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
主要解决: 它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
如何解决: 树枝和叶子实现统一接口,树枝内部组合该接口。
关键代码: 树枝内部组合该接口,并且含有内部属性 List,里面放 Component。
应用实例: 1、算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。 2、在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点: 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
使用场景: 部分、整体场景,如树形菜单,文件、文件夹的管理。
注意事项: 定义时为具体类。
2 组合模式详解
2.1 组合模式结构
组合模式的结构如图所示:
由图可知,组合模式包含以下3个角色。
- Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
- Leaf(叶子构件):它在组合结构中表示叶子结点对象,叶子结点没有子结点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过抛出异常、提示错误等方式进行处理。
- Composite(容器构件):它在组合结构中表示容器结点对象,容器结点包含子结点,其子结点可以是叶子结点,也可以是容器结点,它提供一个集合用于存储子结点,实现类在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子结点的业务方法。
2.2 组合模式实现
对于组合模式中的抽象构件角色,其典型代码如下:
|
|
对于组合模式中的叶子构件角色,其典型代码如下:
|
|
对于组合模式中的容器构件角色,其典型代码如下:
|
|
2.3 组合模式应用举例
题目描述
某软件公司欲开发一个杀毒(Antivirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现使用组合模式来设计该杀毒软件的整体框架。
其中,AbstractFile充当抽象构件类,Folder充当容器构件类,ImageFile、TextFile和VideoFile充当叶子构件类。
代码
3 透明组合模式和安全组合模式
组合模式根据抽象构件的定义形式又可以分为透明组合模式和安全组合模式。
透明组合模式
根据结构图我们可以看出抽象构件Component中声明了所有用于管理成员对象的方法,包括add()、remove(),以及getChild()等方法,在客户端看来,叶子对象与容器对象所提供的方法是一致的,客户端可以一致地对待所有的对象,缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的。
安全组合模式
根据结构图可以看出抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法,对于叶子对象,客户端不可能调用到这些方法。缺点是不够透明,客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。但在实际应用中,安全组合模式的使用频率也非常高。