组合模式关注那些存在于叶子构件和容器构件的结构以及它们的组织形式,叶子构件中不能包含成员对象,容器构件中可以包含成员对象,这些成员对象可能是叶子构件对象,也可能是容器构件对象。这些对象可以构成一个树形结构,组合模式是用面向对象的方法处理树形结构。
模式动机
在Windows操作系统的文件目录结构包含文件和文件夹两类对象,其中在文件夹可以包含子文件夹,也可以包含文件。文件夹是容器类,而不同类型的各种文件是成员类,也称为叶子类,一个文件夹也可以作为另一个更大的文件夹的成员。组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以做到一致对待容器对象和叶子对象。
模式定义
组合多个对象形成树形结构以表示“整体-部分”的结构层次。
模式结构
-
Component(抽象构件)
抽象构件可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件在定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
-
Leaf(叶子构件)
叶子构件在组合结构中表示叶子节点对象,叶子节点没有子节点,实现在抽象构件中定义的行为。对于访问及管理子构件的方法,可以通过异常等方式进行处理。
-
Composite(容器构件)
容器构件在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
-
Client(客户类)
客户类是可以通过抽象构件接口访问和控制组合构件中的对象。
模式分析
组合模式的关键是定义一个抽象构件类,它既可以代表叶子,也可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底是叶子还是容器,可以对其进行统一处理。
一般抽象构件类设计为接口或抽象类,将所有子类共有方法声明和实现放在抽象构件类中。对于客户端编程,将针对抽象构件编程,而无须关心其具体子类是容器构件还是叶子构件。
public abstract class Component {
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract Component getChild(int i);
public abstract void operation();
}
继承抽象构件的是叶子构件则典型代码如下,叶子构件需要实现抽象构建类中声明的所有方法,包括业务方法以及管理和访问子构件的方法,但是叶子构件不包含子构件,因此在在客户端调用叶子构件的子构件管理和访问方法时需提供异常处理或错误提示。
public class Leaf extends Component {
@Override
public void add(Component c) {
//异常处理或错误提示
}
@Override
public void remove(Component c) {
//异常处理或错误提示
}
@Override
public Component getChild(int i) {
//异常处理或错误提示
return null;
}
@Override
public void operation() {
//实现代码
}
}
容器构件需要实现抽象构建类中声明的所有方法,包括具体实现业务方法以及管理和访问子构件的方法。由于容器构件充当的是容器角色,包含成员构件,因此它将调用其成员构件的业务方法,使用递归算法,即在容器构件的operation()方法中递归调用其成员构件的operation()方法。
public class Composite extends Component {
private ArrayList list = new ArrayList();
@Override
public void add(Component c) {
list.add(c);
}
@Override
public void remove(Component c) {
list.remove(c);
}
@Override
public Component getChild(int i) {
return (Component) list.get(i);
}
@Override
public void operation() {
for (Object o : list) {
((Component) o).operation();
}
}
}
模式优缺点
组合模式优点:
- 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,方便对层次结构进行控制
- 客户端可以一致的适用组合结构或其中单个对象,用户不必关心自己处理的是单个对象还是整个组合结构
- 定义了包含叶子对象和容器对象的类的层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构
- 更容易在组合体内加入对象构件,客户端不必因为加入新的对象构件而更改原有代码
组合模式缺点:
- 使设计变得更加抽象
- 增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制
模式适用环境
在以下情况下可以使用组合模式:
- 需要表示一个对象整体或部分层次,在具有整体和部分的层次结构中,希望忽略整体与部分的差异,一致地对待它们
- 客户能忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节
- 对象的结构是动态且复杂程度不一致,但客户需要一致地处理它们
透明组合模式与安全组合模式
组合模式根据抽象构件类的定义形式,又可分为透明组合模式和安全组合模式
-
透明组合模式
透明组合模式中,抽象构件Component中声明所有用于管理成员对象的方法,这样做的好处是确保所有构件类都有相同的接口。在客户端看来,叶子对象与容器对象所提供的方法是一致的,客户端可以相同地对待所有的对象。缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的。
-
安全组合模式
安全组合模式中抽象构件Component中没有声明任何用于管理成员对象的方法,而是在Composite类中声明这些用于管理成员对象的方法。对于叶子对象,客户端不可能调用到管理成员对象的方法。缺点是不够透明,客户端不能完全针对抽象编程。