组合模式关注那些存在叶子构件和容器构件的结构以及它们的组织形式,叶子构件中不能包含成员对象,而容器构件中可以包含成员对象,这些成员对象可能是叶子构件对象,也可能是容器构件对象。这些对象可以构成一个树形结构,组合模式是用面向对象的方式来处理树形结构,它为叶子构件和容器构件提供了一个公共的抽象构件类,客户端可以针对该抽象类进行处理,而无须关心所操作的是哪种类型的对象。
动机
容器对象和叶子对象在功能上有区别,在使用这些对象的客户端代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下客户端希望能一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。如何将容器对象和叶子对象进行递归组合,使得用户在使用时无需对它们进行区分,可以一致的对待容器对象和叶子对象。
定义
组合多个对象形成树形结构以表示“整体-部分”的结构层次。**组合模式对叶子对象和容器对象的使用具有一致性。**组合模式又可以称为“整体-部分”模式,属于对象的结构模式,它将对象组织到树结构中,可以用来描述整体和部分的关系。
案例
使用组合模式设计一个杀毒软件(AntiVirus)的框架,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒,文件种类包括文本文件TextFile、图片文件ImageFile、音频视频文件MediaFile。绘制类图并编程实现。
类图如下:
代码如下:
public abstract class File {
abstract void add(File file);
abstract void remove(File file);
abstract void antivirus();
}
public class ImageFile extends File {
private String fileName;
public ImageFile(String fileName){
this.fileName=fileName;
}
@Override
void add(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void remove(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void antivirus() {
System.out.println("图片文件:"+fileName+"正在进行杀毒");
}
}
public class MediaFile extends File {
private String fileName;
public MediaFile(String fileName){
this.fileName=fileName;
}
@Override
void add(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void remove(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void antivirus() {
System.out.println("音频视频文件:"+fileName+"正在进行杀毒");
}
}
public class TextFile extends File {
private String fileName;
public TextFile(String fileName){
this.fileName=fileName;
}
@Override
void add(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void remove(File file) {
System.out.println("文件不能执行此操作");
}
@Override
void antivirus() {
System.out.println("文本文件:"+fileName+"正在进行杀毒");
}
}
public class Folder extends File {
private ArrayList<File> list;
private String folderName;
public Folder(String name){
list=new ArrayList<>();
this.folderName=name;
}
@Override
void add(File file) {
list.add(file);
}
@Override
void remove(File file) {
list.remove(file);
}
@Override
void antivirus() {
for(File file:list){
file.antivirus();
}
}
}
测试代码:
public class Main {
public static void main(String[] args) {
TextFile t1=new TextFile("A.txt");
TextFile t2=new TextFile("B.txt");
ImageFile i1=new ImageFile("A.png");
MediaFile m1=new MediaFile("A.avi");
MediaFile m2=new MediaFile("B.avi");
MediaFile m3=new MediaFile("C.avi");
Folder f1=new Folder("A");
Folder f2=new Folder("B");
f1.add(t1);f1.add(m1);f1.add(m3);f1.add(f2);
f2.add(i1);f2.add(t2);f2.add(m2);
f1.antivirus();
}
}
测试结果:
总结
我认为组合模式解决的最重要的问题是:对于用户来说,无需区分是叶子节点还是容器结点,都可以通过抽象类进行同等对待。例如对于一个文件来说,它可能是一个叶子节点,也可能是一个容器节点,包含很多其他的文件,但是当用户需要查询某个文件的具体信息时,可以无需区分它是不是文件夹,只需调用自己需要的方法进行操作。