一、基本概念
树形结构在软件中随处可见,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等等。
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
(1)组合关系:佳丽三千只属于一个皇帝。
(2)聚合关系:一个老师有很多学生,一个学生有多个老师。
二、角色组成
- Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
- Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
- Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
三、实例代码
- 抽象构件角色
package cpmposite03;
/**
* @author:xiaofa
* @date2020/4/2811:12
* @Description:抽象构件
*
*/
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.java
package cpmposite03;
/**
* @author:xiaofa
* @date2020/4/2811:14
* @Description:叶子构件
*
*/
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.java
package cpmposite03;
/**
* @author:xiaofa
* @date2020/4/2811:14
* @Description:叶子构件
*
*/
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+"进行杀毒");
}
}
- 叶子构件:Video.java(同上此处省略)
...
@Override
public void killVirus() {
System.out.println("----对文本文件----"+name+"进行杀毒");
...
- 容器构件:Folder.java
package cpmposite03;
import composite02.Composite;
import java.util.ArrayList;
/**
* @author:xiaofa
* @date2020/4/2811:18
* @Description:容器构件
*/
class Folder extends AbstractFile {
//名字属性
private String name;
//一个参数的构造方法
public Folder(String name) {
this.name = name;
}
//构建容器,用于存储AbstactFile类型的成员
ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();
@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+"进行杀毒");
for (Object object:fileList){
((AbstractFile)object).killVirus();
}
}
}
- 客户端:Client.java
package cpmposite03;
import composite01.Folder;
import composite01.ImageFile;
import composite01.TextFile;
/**
* @author:xiaofa
* @date2020/4/2715:15
* @Description:
*/
public class Client {
public static void main(String[] args) {
Folder folder1 , folder2 , folder3 ;
folder1 = new Folder("XiaoFa的资料");
folder2 = new Folder("图像文件");
folder3 = new Folder("文本文件");
ImageFile image1 , image2 , image3;
image1 = new ImageFile("小女.jpg");
image2 = new ImageFile("杨过河.jpg");
TextFile text1 ,text2;
text1 = new TextFile("菊花宝典.txt");
text2 = new TextFile("九阴真茎.dox");
folder2.addImageFile(image1);
folder2.addImageFile(image2);
folder3.addTextFile(text1);
folder3.addTextFile(text2);
folder1.addFolder(folder2);
folder1.addFolder(folder3);
folder3.killVirus();
image1.killVirus();
}
}
运行结果
-
对folder1进行消毒
-
对folder3和image1进行消毒
四、透明组合和安全组合模式
由于AbstractFile中声明了大量用于管理和访问成员构件的方法,如add(),remove等,因此要新增文件类实现这些方法,以提供错误提示和异常处理。这里给出两种实现方式。
- 方式一:将叶子构件的add()、remove()的实现代码一直抽象类中,然后将成员构件的相关的代码删除。
package composite04_1;
abstract class AbstractFile {
public void add(AbstractFile file){
System.out.println("对不起,不支持该方法!");
};
public void remove(AbstractFile file){
System.out.println("对不起,不支持该方法!");
};
public AbstractFile getChild(int i){
System.out.println("对不起,不支持该方法!");
return null;
};
public abstract void killVirus();
}
- 方式二:在抽象构件AbstractFile中不声明访问和管理成员的方法。
package composite04_1;
abstract class AbstractFile_2 {
public abstract void killVirus();
}
小结
- 透明组合模型:
方式一是透明组合模型,抽象构件中声明所有用于管理成员对象的方法,保证了所有构建类具有相同的接口,是组合模式的标准形式。
但缺点不够安全,因为叶子对象不可能有下一层次的对象,因此add()、remove()等方法没有意义,编译不出错,运行和调用可能出错。 - 安全组合模型:
方式二是安全组合模型,在抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明并实现这些方法。做法安全,因为根本不像叶子对象提供这些管理方法,叶子对象客户端都不会调用这些方法。
但缺点不够透明,因为叶子构件和容器构件具有不同的方法,容器中方法没有在抽象构建中定义,客户端不能完全针对抽象编程。
适用场景
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
参考书籍《Java设计模式-刘伟第二版》