一、什么是组合模式
组合(Composite)模式是一种对象的行为模式。将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式的本质:统一叶子对象和组合对象。
组合模式的目的:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。
二、组合模式的适用性
在开发中, 我们经常可能要递归构建树状的组合结构,比如以下的图书目录树:
仔细观察上面的类别树,有以下几个明显的特点。
• 有一个根节点,它没有父节点,它可以包含其他的节点。
• 树枝节点,有一类节点可以包含其他的节点,称之为树枝节点,比如“章”、“节”。
• 叶子节点,有一类节点没有子节点,称之为叶子节点,比如“页”
如果碰到类似上面这种,需使用对象树来描述或实现的功能,都可以考虑使用组合模式
三、组合模式的结构
组合模式涉及的角色及其职责如下:
抽象组件(Node)角色:为组合对象和叶子对象声明公共的接口,让客户端可以通过这个接口来访问和管理整个对象树,并可以为这些定义的接口提供缺省的实现。
组合对象(BranchNode)角色:通常会存储子组件(组合对象、叶子对象),定义包含子组件的那些组件的行为,并实现在抽象组件中定义的与子组件有关的操作,例如子组件的添加(addChild)和删除(removeChild)等。
叶子对象(Leaf)角色:定义和实现叶子对象的行为,并且它不再包含其他的子节点对象。
组合模式结构示意源代码如下:
/**
* 抽象的组件对象,为组合中的对象声明接口,实现接口的缺省行为
*/
abstract class Node {
abstract public void p();
}
接下来看看组合类的定义,示意代码如下。
/**
* 树干节点
*/
class BranchNode extends Node {
List<Node> nodes = new ArrayList<>();
String name;
public BranchNode(String name) {this.name = name;}
public void add(Node n) {
nodes.add(n);
}
@Override
public void p() {
System.out.println(name);
}
}
再来看看叶子类的定义,示例代码如下。
/**
* 树叶节点
*/
class LeafNode extends Node {
String name;
public LeafNode(String name) {this.name = name;}
@Override
public void p() {
System.out.println(name);
}
}
在客户端中使用Component接口来操作组合对象结构,示意代码如下。
public class Main {
public static void main(String[] args) {
BranchNode root = new BranchNode("root");
BranchNode chapter1 = new BranchNode("chapter1");
BranchNode chapter2 = new BranchNode("chapter2");
Node chapter3 = new LeafNode("chapter3");
Node c11 = new LeafNode("c11");
Node c12 = new LeafNode("c12");
BranchNode b21 = new BranchNode("section21");
Node c211 = new LeafNode("c211");
Node c212 = new LeafNode("c212");
root.add(chapter1);
root.add(chapter2);
root.add(chapter3);
chapter1.add(c11);
chapter1.add(c12);
chapter2.add(b21);
b21.add(c211);
b21.add(c212);
tree(root, 0);
}
static void tree(Node b, int depth) {
for(int i=0; i<depth; i++) System.out.print("--");
b.p();
if(b instanceof BranchNode) {
for (Node n : ((BranchNode)b).nodes) {
tree(n, depth + 1);
}
}
}
}
运行程序打印结果如下:
root
--chapter1
----c11
----c12
--chapter2
----section21
------c211
------c212
--chapter3
从以上的示例可以看出,组合模式的关键就在于抽象组件角色,作为组合对象和叶子对象的父类,这个抽象组件类既可以代表叶子对象,也可以代表组合对象,这样用户在操作的时候,始终是在操作组件对象,不必再去区分是在操作组合对象还是叶子对象,从而使得对叶子对象和组合对象的使用具有了一致性。
四、组合模式的优缺点
使用组合模式的优点:
1) 统一了组合对象和叶子对象。
2) 简化了客户端调用,无须区分操作的是组合对象还是叶子对象。
3) 更容易扩展,有了Component的约束,新定义的Composite或Leaf子类能够很容易地与已有的结构一起工作。
使用组合模式的缺点: 很难限制组合中的组件类型。