目录
1、组合模式的定义
组合模式又名部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
2、组合模式例子
生活中其实很多地方都体现了组合模式,最典型的便是我们使用的电脑里面的“目录”就是经典的组合模式。
目录由各种文件(如txt、word、PDF)、文件夹组成,一开始有个根文件夹,文件夹里面可以有各式各样的文件和文件夹,如下图:
这实际上就是典型的组合模式。下面我们来基于这个例子实现一下组合模式。
3、组合模式实现
3.1 组合模式的结构
组合模式主要包含三种角色:
- 抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
- 树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
- 叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
3.2 组合模式的分类
在使用组合模式时,根据抽象构件类的定义形式,我们可将组合模式分为透明组合模式和安全组合模式两种形式:(看不懂这一块可以先看代码示例,本文拿透明组合模式来举例,安装组合模式和这种模式差不多,大家看完就明白了。)
①透明组合模式:
透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在我们接下来示例中AbstractFile声明了 createFile、deleteFile 、getFiles方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。
透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能
有下一个层次的对象,即不可能包含成员对象,因此为其提供 createFile()、deleteFile() 、getFiles()等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
②安全组合模式:
在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点
FileFolders类中声明并实现这些方法。
安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
3.3 组合模式代码实现(透明组合模式)
实际上我们上述的文件夹和各种文件(如word,PDF等)组成,因此我们可以这样来设计:设计一个抽象文件类(抽象根节点)、文件夹类(树枝节点)、普通文件类(叶子结点)。
抽象文件类(抽象根节点):
public abstract class AbstractFile { //抽象文件类
private String name; //文件名
private List<AbstractFile>list;//该结点下的子节点
public void createFile(){ //在该节点下创建子节点
throw new UnsupportedOperationException();
}
public void deleteFile(){ //删除子节点
throw new UnsupportedOperationException();
}
public void getFiles(){ //获得所有子结点
throw new UnsupportedOperationException();
}
public void setName(String name){ //重置名称
this.name=name;
}
public String getName() { //获取名称
return name;
}
public AbstractFile(String name) {
this.name = name;
}
}
文件夹类(树枝节点):
public class FileFolders extends AbstractFile { //文件夹类
public FileFolders(String name) {
super(name);
}
//注意!!!
//只有文件夹才有子节点,所以文件夹可以重写这些方法的功能,否则调用会报错
//由于为了方便理解,我们就模拟创建子节点、删除子节点了。
@Override
public void createFile() {
//模拟创建子节点
System.out.println("创建子节点");
}
@Override
public void deleteFile() {
//模拟删除子节点
System.out.println("删除子节点");
}
@Override
public void getFiles() {
//模拟获取所有子节点
System.out.println("获取所有子节点");
}
}
普通文件类:
public class myFile extends AbstractFile { //普通文件类
public myFile(String name) {
super(name);
}
}
可见,此时如果我们想创建一个目录,只需先创建一个文件夹类作为根目录,然后可以对文件夹类进行增、删子节点的操作,这实际上和我们大家平时使用电脑是一样的,只不过我们通过组合模式将他们按“树形结构”组合了起来而已。
当然!这里我们实现的是透明组合模式,也就是虽然“普通文件类”也能有 createFile()、deleteFile() 、getFiles()等方法,这样好处我们上面说了,大家可以去看看,但是如果普通文件类调用这些方法,实际上是调用“AbstractFile类”的方法,而我们声明这些方法时,是直接抛出一个“UnsupportedOperationException”异常,因此保证了普通文件类无法创建子文件,这也是符合我们常理的,如下我们模拟一下上述操作:
测试代码:
public class Test {
public static void main(String[] args) {
myFile file = new myFile("一个普通文件");
file.createFile();
}
}
运行结果:
4、组合模式的优点
- 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
- 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
5、组合模式使用场景
组合模式正是应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方。比如:文件目录显示,多级目录呈现等树形结构数据的操作。