组合模式
-
组合模式动机及定义
1.1模式动机
在面向对象系统中,我们常常会遇到一类具有“容器”特征对对象——即它们在充当普通对象对同时,又可作为其他对象对容器,这些对象成为容器对象,而那些只能充当普通对象的对象则称为叶子对象。在容器对象中即可以包含叶子对象,又可以包含容器对象,为了更好地解决容器对象和叶子对象之间的关系,使之操作更加简单,我们需要学习一种新的结构设计模式,即组合模式。组合模式实际要解决的问题是:对整体与个体的操作应一致,类似对文件夹与文件对操作。
组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象,这就是组合模式的模式动机。
1.2模式定义
组合模式定义(Composite Pattern)定义:组合多个对象形成树形结构以表示“部门-整体”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。组合模式又可以称为“部分-整体”(Part-Whole)模式,属于对象的结构模式,它将对象组织到树形结构中,可以用来描述整体与部分的关系。
英文定义:“Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.”
2.组合模式结构与分析
组合模式的核心在于引入了一个抽象类,它既是叶子类的父类,也是容器类的父类。
2.1模式结构
1.Component(抽象构件)
抽象构件可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类公有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
2.Leaf(叶子构件)
叶子构件在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
3.Composite(容器构件)
容器构件在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义对行为,包括那些访问及管理子构件对方法,在其业务方法中可以递归调用其子节点对业务方法。
4.Client(客户类)
客户类可以通过抽象构件接口访问和控制构件中的对象。
3.组合模式实例与解析
题目:使用组合模式设计一个杀毒软件(AntiVirus)的框架,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒,文件种类包括文本文件TextFile、图片文件ImageFile、音频视频文件MediaFile。绘制类图并编程实现。
3.1实例类图
3.2实例代码及解释
1.抽象构件类AbstractFile(抽象文件类)
package composite_pattern;
/*
* 抽象文件类:抽象构件
* */
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();
}
AbstractFile是抽象构件类,在其中声明了killVirus()方法,在其子类中实现该方法。
2.叶子构件类VideoFile(视频文件类)
package composite_pattern;
/*
* 视频文件类:叶子构件
* */
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("正在对Video文件'" + name + "'进行杀毒操作");
}
}
VideoFile类是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();
3.叶子构件类TextFile(文本文件类)
package composite_pattern;
/*
* 文本文件类:叶子构件
* */
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("正在对Text文件'" + name + "'进行杀毒操作");
}
}
TextFile类也是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();
4.叶子构件类ImageFile(图片文件类)
package composite_pattern;
/*
* 图像文件类:叶子构件
* */
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("正在对Image文件'" + name + "'进行杀毒操作");
}
}
ImageFile类也是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();
5.容器构件类Folder(文件夹类)
package composite_pattern;
/*
* 文件夹类:容器类
* */
import java.util.ArrayList;
public class Folder extends AbstractFile {
private ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();
private String name;
public Folder(String name) {
this.name = name;
}
@Override
public void add(AbstractFile file) {
fileList.add(file);
}
@Override
public void remove(AbstractFile file) {
fileList.remove(file);
}
@Override
public AbstractFile getChild(int i) {
return (AbstractFile)fileList.get(i) ;
}
@Override
public void killVirus() {
System.out.println("正在对文件夹'"+ name + "'进行杀毒");
//递归调用成员构件的killVirus()方法
for (Object obj:fileList){
((AbstractFile)obj).killVirus();
}
}
}
Folder类是容器构件类,在其代码中需要注意三个要点:首先它定义了一个抽象构件类型的集合,此处使用ArrayList来实现;它提供了用于操作子构件的相关方法,如增加子构件、删除子构件和获取子构件等方法;它实现了在抽象构件中定义的killVirus方法,且在该方法的内部递归调用其子构件的killVirus()方法。
3.3辅助代码
客户端测试类Client如下:
package composite_pattern;
public class Client {
public static void main(String[] args) {
//针对抽象构件编程
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;
folder1 = new Folder("我的文件");
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);
//从“我的文件”节点开始进行杀毒操作
folder1.killVirus();
}
}
在客户端代码中,实例化了一些叶子构件即文件类,也实例化了一些容器构件即文件夹类,通过文件夹类的add()方法可以将子构件添加到文件夹中,其子构件可以是文件对象,也可以是文件夹对象。
3.4结果
4.组合模式效果与应用
4.1 组合模式的优点
(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易,因为它让客户忽略了层次的差异,而它的结构又是动态的,提供了对象管理的灵活接口,因此组合模式可以方便地对层次结构进行控制。
(2)客户端调用简单,客户端可以一致地使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,简化了客户端代码。
(3)定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
(4)更容易在组合体内加人对象构件,客户端不必因为加人了新的对象构件而更改原有代码。
4.2组合模式的缺点
(1)使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。
(2)增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检杳来实现,这个实现过程较为复杂。
4.3模式适用环境
在以下情况可以使用组合模式。
- 需要表示一个对象的整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
- 让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。
- 对象的结构是动态的并且复杂程度不一样,但客户需要一致地处理它们。