概念
组合模式,也被称作合成型模式或者Composite模式,将对象组合成树形结构以表示“整体-部分”的层次关系。组合涉及的是一组对象,其中有的对象可能含有其他的对象(对象群组),而有的只是单个对象(叶子Leaf)。
组合模式涉及目的:让用户能够用统一的接口来处理单个对象以及组合对象。
例如:文件系统,一个文件路径下既包含单独的文件,也包含其他的文件夹,而这些文件夹下又包含别的文件和文件夹。这是一个典型的组合模式的树形结构,文件夹==组合对象,文件==单个对象。而用户可以通过类似的方式遍历文件夹和文件。
组合模式角色结构
- 抽象构建角色(Component):定义共有接口及其默认行为。
- 树叶结构角色(Leaf):代表参加组合的树叶对象(单个对象),定义参加组合的原始对象的行为。
- 树枝构建角色(Componsite):有子对象的对象(对象组合),定义树枝结构对象行为。
组合模式的两种形式
- 透明方式:Component里面申明所有用来管理子对象的方法
- 优点:所有构建类都有相同的接口,组合对象与单个对象的区别在接口层次上消失了,客户端可以同等的对待所有的对象;
- 缺点:不安全,树叶类对象没有下一个层次的对象,这样的话有一些方法(add(),remove())等操作没有意义,这种编译是不会报错,但运行时会出错。
- 安全方式:在Componsite类里声明所有的用来管理子类对象的方法。
- 优点:这样做是安全的,因为树叶类型的对象没有管理子类对象的方法,如果客户端对树叶类对象使用这些方法时,编译时会出错,这样程序就到不了运行阶段。
- 缺点:不够透明,树叶类和合成类具有不同的接口。
使用组合模式的注意事项
- 明显的给出父类对象的引用,在子对象给出父对象的引用,这样比较容易遍历所有的父对象;
- 在通常的系统里,可以使用享元模式实现构件的共享,但由于组合模式中包含对父对象的引用,该共享不容易实现;
- 有时系统需要对一个树枝构件遍历很多次,这时可以把遍历子构件的结果作为缓存暂时存放在父构件里;
- 可以用Vector或者其他数组的聚集存储子对象;
- 客户端不应该直接调用树叶类,应该由其父类向树叶类进行委派,这样可以增强代码的复用性。
示例说明
简单实现文件系统的文件/目录结构
/**
* Component接口
*/
public interface Root {
boolean addFile(Root file);
boolean removeFile(Root file);
void disPlay();
List<Root> getFile();
}
/**
* Componsite实现-其种包含子文件夹和文件
*/
class Folder implements Root{
String name;
List<Root> folder;
public Folder(String name) {
this.folder = new ArrayList<>();
this.name = name;
}
@Override
public boolean addFile(Root file) {
return folder.add(file);
}
@Override
public void disPlay() {
System.out.println(name);
for (Root file : folder) {
if(file instanceof Folder){
System.out.print("|__");
}
file.disPlay();
}
}
@Override
public List<Root> getFile() {
return folder;
}
@Override
public boolean removeFile(Root file) {
return folder.remove(file);
}
}
/**
* Leaf-存在于文件夹下的文件
*/
class File implements Root{
String name;
public File(String name) {
this.name = name;
}
@Override
public boolean addFile(Root file) {
return false;
}
@Override
public void disPlay() {
System.out.println(" |__" + name);
}
@Override
public List<Root> getFile() {
return null;
}
@Override
public boolean removeFile(Root file) {
return false;
}
}
class CompositeClient{
public static void main(String[] args){
Root root1 = new Folder("c://");
Root root2 = new Folder("d://");
Root win = new Folder("windows");
Root sys = new Folder("system");
Root hw = new File("HelloWorld.java");
Root picture = new File("picture.png");
root1.addFile(win);
root1.addFile(sys);
win.addFile(hw);
win.addFile(picture);
root1.disPlay();
System.out.println("****************************");
root1.removeFile(win);
root1.disPlay();
System.out.println("============================");
root2.disPlay();
}
}
运行结果:
c://
|__windows
|__HelloWorld.java
|__picture.png
|__system
****************************
c://
|__system
============================
d://
应用场景
- 需要描述对象的部分和整体的等级结构
- 需要客户端忽略掉个体构建和组合构建的区别