定义
组合模式又叫部分-整体模式,组合多个对象形成树形结构以表示“整体-部分”的结构层次。
组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。同时它也模糊了简单元素(叶子对象)和复杂元素(容器对象)的概念,使得客户能够像处理简单元素一样来处理复杂元素,从而使客户程序能够与复杂元素的内部结构解耦。
组合模式包含角色:
-
Component :组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理
Component
子部件。 -
Leaf:叶子对象。叶子结点没有子结点。
-
Composite:容器对象,定义有枝节点行为,用来存储子部件,在
Component
接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
从模式结构中我们看出了叶子节点和容器对象都实现 Component
接口,这也是能够将叶子对象和容器对象一致对待的关键所在。
优缺点
优点:
-
更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。
-
可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。
-
客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。
-
定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
缺点
- 使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联
实例
以文件系统为例。
定义文件抽象类:
public abstract class File {
public File(String name){
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void display();
}
文本文件、图像文件、视频文件对象:
public class TestFile extends File {
public TestFile(String name) {
super(name);
}
@Override
public void display() {
System.out.println("文本文件,文件名:" + super.getName());
}
}
public class ImageFile extends File {
public ImageFile(String name) {
super(name);
}
@Override
public void display() {
System.out.println("图像文件,文件名:" + super.getName());
}
}
public class VideoFile extends File {
public VideoFile(String name) {
super(name);
}
@Override
public void display() {
System.out.println("视频文件,文件名:" + super.getName());
}
}
文件夹对象:
public class Folder extends File {
private List<File> fileList = new ArrayList<>();
public Folder(String name) {
super(name);
}
/**
* @desc 向文件夹中添加文件
* @param file
* @return void
*/
public void add(File file){
fileList.add(file);
}
/**
* @desc 从文件夹中删除文件
* @param file
* @return void
*/
public void remove(File file){
fileList.remove(file);
}
/**
* 浏览文件夹中的文件
*/
@Override
public void display() {
for(File file : fileList){
file.display();
}
}
}
调用:
public static void main(String[] args) {
// 组装文本目录
Folder testFolder = new Folder("文本目录");
testFolder.add(new TestFile("文本文件1"));
testFolder.add(new TestFile("文本文件2"));
// 组装图片目录
Folder imageFolder = new Folder("图片目录");
imageFolder.add(new ImageFile("图片文件1"));
imageFolder.add(new ImageFile("图片文件2"));
// 组装视频目录
Folder videoFolder = new Folder("视频目录");
videoFolder.add(new VideoFile("视频文件1"));
videoFolder.add(new VideoFile("视频文件2"));
// 组装根目录
Folder rootFolder = new Folder("根目录");
rootFolder.add(testFolder);
rootFolder.add(imageFolder);
rootFolder.add(videoFolder);
// 浏览目录
System.out.println("=========浏览根目录=========");
rootFolder.display();
System.out.println("=========浏览文本目录=========");
testFolder.display();
System.out.println("=========浏览图片目录=========");
imageFolder.display();
System.out.println("=========浏览视频目录=========");
videoFolder.display();
}
控制台输出:
=========浏览根目录=========
文本文件,文件名:文本文件1
文本文件,文件名:文本文件2
图像文件,文件名:图片文件1
图像文件,文件名:图片文件2
视频文件,文件名:视频文件1
视频文件,文件名:视频文件2
=========浏览文本目录=========
文本文件,文件名:文本文件1
文本文件,文件名:文本文件2
=========浏览图片目录=========
图像文件,文件名:图片文件1
图像文件,文件名:图片文件2
=========浏览视频目录=========
视频文件,文件名:视频文件1
视频文件,文件名:视频文件2