组合模式(Composite Pattern)
将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,组合模式定义了如何将容器对象和叶子对象进行递归组合,使用户对单个对象和组合对象具有一致的访问性。
组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
组合模式主要包含如下几个角色:
- 抽象构件(Component)角色:组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。
- 树叶构件(Leaf)角色:叶子对象。叶子结点没有子结点。
- 树枝构件(Composite)角色 / 中间构件:容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
实例
请求从上到下沿着树进行传递,直到树的尽头。作为客户,只需要关心树最顶层的组合对象,客户只需要请求这个组合对象,请求便会沿着树往下传递,依次到达所有的叶对象。
抽象构件
//
public interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
树叶构件
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
}
public void remove(Component c) {
}
public Component getChild(int i) {
return null;
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
树枝构件
public class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
Test
public class MyTest {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
程序运行结果如下:
树叶1:被访问!
树叶2:被访问!
树叶3:被访问!
以上为透明式组合模式。客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。对于客户而言,都是Compoent对象,具体的类型对于客户端而言是透明的,是无须关心的。
其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,调用会导致编译错误。因此透明方式会带来一些安全性问题。
因此还有一种安全模式的组合模式。
它要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件类中。安全式合成模式的缺点是不够透明,因为树叶类和树枝类将具有不同的接口。具体实现方法为:去掉抽象类(Component)的方法,让树枝类(Composite)添加对应的方法,树叶类(Leaf)不写其他方法。
但是对于合成模式而言,在安全性和透明性上,会更看重透明性,毕竟合成模式的目的是:让客户端不再区分操作的是树枝对象还是树叶对象,而是以一个统一的方式来操作。
因此在使用合成模式的时候,建议多采用透明性的实现方式。