注意:本文内容是对 该博客 的学习。
组合模式
组合模式的概念
组合模式的定义
组合(composite)模式,有时又叫做部分-整体模式
,它是一种将对象组合成树状的层次结构的模式
,用来表示“部分-整体”的关系,使用户对单个对象和组合对象有一致的访问性。
组合模式的结构
抽象构件(componet)角色: 它的主要作用是为树叶构件和树枝构件声明公共接口
,并实现他们的缺省行为
(就是默认的行为,通常指共同拥有的方法等)。
在透明式的组合模式
中抽象构件还声明访问和管理子类的接口;在安全式的组合模式
中不声明访问和管理子类的接口,管理工作由树枝构建完成。
树叶构件(leaf)角色: 是组合中的叶节点对象,它没有子节点
,用于实现抽象构件角色中声明的公共接口。
树枝构件(composite)角色: 是组合中的分支节点对象,它有子节点
。它实现了抽象构建角色中声明的接口。它的主要作用是存储和管理子部件
,通常包含add(),remove(),getChild()等方法。
我对组合模式的理解是:组合模式利用了树的特性。因为树可以递归到每一个子节点
,因而当我们对树的根进行操作的时候,就可以递归的对每一个节点进行操作,这样,我们可以把树的根看做这颗树的代表,即整体,把其他的实体类(leaf和composite)作为树的节点,当我们操作这棵树的根的时候,就相当于操作每一个节点了(递归的访问每一个节点)。
比如我们要树和客户A通话,只需要调用树的根与与A通话的方法,这个方法递归的遍历每一个节点,最终的效果是所有的节点都与A通话了。
透明式的组合模式
在该方式中,由于抽象构件中声明了所有子类中的全部方法
,所以客户端无需区别树叶对象和树枝对象,对于客户端来说是透明的
,下面是其结构图:
安全式的组合模式
在该方法中,将管理子构件的方法移到树枝构件中
,抽象构件和树叶构件没有对子对象的管理方法,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性
,下面是其结构图:
我对这两个模式的理解:二者的区别是透明模式里对子对象操作的方法在抽象类
里就有,而安全式里对子对象操作的方法只有树枝构件
有。顾名思义,因为在透明式的组合模式中,用户直接操作根节点就完事了,所以这个模式对于客户是透明的,但这样不安全,因为抽象类中的操作子节点的方法在树叶节点中根本没有实现,如果客户对树叶节点调用操作子节点方法,就会抛出异常。
换言之,把操作子节点的方法放在树枝构件里,不放在抽象构件,就可以解决这个安全问题,因而它叫做安全式的组合模式。而安全式的缺点就是,用户在调用的时候,得分清楚自己调用的是树叶对象还是树枝对象,这就不够透明了。
组合模式的例子
该博客 中用了一个非常好理解的例子来帮助我们理解组合模式,那就是文件夹的管理。
我们来实现一个简单的目录树,有文件夹和文件两种类型,首先需要一个抽象构件类,声明了文件夹类和文件类需要的方法。
//文件夹-文件 就是组合模式的一种典型用法
public abstract class Component {
public String getName() {
throw new UnsupportedOperationException("不支持获取名称操作!");
};
public void add(Component component){
throw new UnsupportedOperationException("不支持添加操作!");
};
public void remove(Component component){
throw new UnsupportedOperationException("不支持删除操作!");
};
public String getContent(){
throw new UnsupportedOperationException("不支持获取内容操作!");
};
public void print(){
throw new UnsupportedOperationException("不支持打印操作!");
};
}
实现一个文件夹类 Folder,继承 Component,定义一个 List 类型的componentList属性,用来存储该文件夹下的文件和子文件夹,并实现 getName、add、remove、print等方法
//文件夹就是树枝节点(composite)
public class Folder extends Component {
private String name;
private List<Component> componentList = new ArrayList<Component>();
private Integer level;
public Folder(String name) {
this.name = name;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return this.name;
}
@Override
public void add(Component component) {
// TODO Auto-generated method stub
this.componentList.add(component);
}
@Override
public void remove(Component component) {
// TODO Auto-generated method stub
this.componentList.remove(component);
}
@Override
public void print() {
System.out.println(this.getName());
if(this.level == null) {
this.level = 1;
}
String prefix = "";
for(int i = 0; i < this.level; ++i) {
prefix += "\t- ";
}
for (Component component : this.componentList) {
if(component instanceof Folder) {
((Folder) component).level = this.level + 1;
}
System.out.print(prefix);
component.print();
}
this.level = null;
// TODO Auto-generated method stub
}
}
文件类 File,继承Component父类,实现 getName、print、getContent等方法
public class File extends Component {
private String name;
private String content;
public File(String name, String content) {
this.name = name;
this.content = content;
}
public String getName() {
return this.name;
}
public String getContent() {
return this.content;
}
public void print() {
System.out.println(this.getName());
}
}
组合模式的优缺点
优点:
- 组合模式使得
客户端代码可以一致地处理单个对象和组合对象
,无需关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 更容易在组合体内加入新的对象
,客户端不会因为加入了新的对象而改变源代码,满足“开闭原则
”。
缺点:
设计较为复杂
,客户端需要花更多的时间理清类之间的层次关系;不容易限制容器中的构件
。