第11章 组合模式
11.1 组合模式概述
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对单个对象和组合对象。
- 部分-整体(Part-Whole)模式
- 对象结构型模式
- 将对象组织到树形结构中,用来描述整体与部分的关系
11.2 组合模式结构与实现
11.2.1 组合模式结构
- Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
- Leaf(叶子构件):它在组合结构中表示叶子结点对象,叶子结点没有子结点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过抛出异常、提示错误等方式进行处理。
- Composite(容器构件) : 它在组合结构中表示容器结点对象,容器结点包含子结点,其子结点可以是叶子结点,也可以是容器结点,它提供一个集合用于存储子结点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子结点的业务方法。
11.2.2 组合模式实现
抽象构件:
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();//业务方法
}
叶子构件:
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() {
//叶子构件具体业务方法的实现
}
}
容器构件:
import java.util.*;
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() {
//容器构件具体业务方法的实现,将递归调用成员构件的业务方法
for (object obj : list) {
((Component) obj).operation();
}
}
}
11.3 组合模式应用实例
实例说明
某软件公司要开发一个杀毒(Antivirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)杀毒。该杀毒软件还可以根据各类文件的特点为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现使用组合模式来设计该杀毒软件的整体框架。
实例类图
- 抽象构件类:
- AbstractFile
- 容器构件类
- Folder
- 叶子构件类
- ImageFile
- TextFile
- VideoFile
实例代码
AbstractFile:抽象文件类,充当抽象构件类。
package designpatterns.composite;
public abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}
ImageFile:图像文件类,充当叶子构件类。
package designpatterns.composite;
public class ImageFile extends AbstractFile {
private String name;
public ImageFile(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对图像文件" + name + "进行杀毒");
}
}
TextFile:文本文件类,充当叶子构件类。
package designpatterns.composite;
public class TextFile extends AbstractFile{
private String name;
public TextFile(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对文本文件" + name + "进行杀毒");
}
}
VideoFile:视频文件类,充当叶子构件类。
package designpatterns.composite;
public class VideoFile extends AbstractFile {
private String name;
public VideoFile(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}
@Override
public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("----对视频文件" + name + "进行杀毒");
}
}
Folder:文件夹类,充当容器构件类。
package designpatterns.composite;
import java.util.ArrayList;
public class Folder extends AbstractFile {
private ArrayList<AbstractFile> files = new ArrayList<AbstractFile>();
private String name;
public Folder(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
files.add(file);
}
@Override
public void remove(AbstractFile file) {
files.remove(file);
}
@Override
public AbstractFile getChild(int i) {
return (AbstractFile) files.get(i);
}
@Override
public void killVirus() {
//模拟杀毒
System.out.println("****对文件夹" + name + "进行杀毒");
for (Object obj : files) {
((AbstractFile) obj).killVirus();
}
}
}
Client:客户端测试类。
package designpatterns.composite;
public class Client {
public static void main(String args[]) {
//针对抽象构件编程
AbstractFile file1, file2, file3, file4, file5, folder1, folder2, folder3, folder4;
folder1 = new Folder("Sunny的资料");
folder2 = new Folder("图像文件");
folder3 = new Folder("文本文件");
folder4 = new Folder("视频文件");
file1 = new ImageFile("小龙女.jpg");
file2 = new ImageFile("张无忌.gif");
file3 = new TextFile("九阴真经.txt");
file4 = new TextFile("葵花宝典. doc");
file5 = new VideoFile("笑傲江湖.rmvb");
folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);
//从"Sunny的资料"结点开始进行杀毒操作
folder1.killVirus();
}
}
结果及分析
11.4 透明组合模式与安全组合模式
透明组合模式
安全组合模式
11.5 组合模式优/缺点与适用环境
11.5.1 组合模式优点
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合开闭原则。
- 为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合可以形成复杂的树形结构,但对树形结构的控制却非常简单。
11.5.2 组合模式缺点
在增加新构件时很难对容器中的构件类型进行限制。有时候希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,在使用组合模式时不能依赖类型系统来施加施加这些约束,因为它们都来自于相同的抽象层,在这种情况下必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
11.5.3 组合模式适用环境
- 在具有整体和部分的层次结构中希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加—些新的类型。