组合模式(composite pattern)
定义
允许你将对象组合成树形结构来表现整体/部分层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
特点
1.结构为树形;
2.节点常见的方法有add()、remove()、getChild()等。
适用范围
树形结构的文件管理。
一般写法
我们来实现如下一棵树:
首先先定义组件抽象类,如下所示:
/**
* 定义组件抽象类,叶节点和组合节点都是组件类的子类,
* 为方法提供默认实现,避免一些组件如叶子节点实现不必要的方法,
* 这里自定义doSomething()实现要对组件的操作
*/
public abstract class Component {
public void add(Component component) {
throw new UnsupportedOperationException();
}
public void remove(Component component) {
throw new UnsupportedOperationException();
}
public Component getChild(int i) {
throw new UnsupportedOperationException();
}
public void doSomething() {
throw new UnsupportedOperationException();
}
}
接着具体节点继承组件抽象类:
import java.util.ArrayList;
import java.util.Iterator;
public class Node extends Component {
// 用于保存节点下面的子节点
ArrayList<Component> components = new ArrayList<>();
// 定义节点ID
int nodeId;
// 构造器传入节点ID
public Node(int nodeId) {
this.nodeId = nodeId;
}
@Override
public void add(Component component) {
components.add(component);
}
@Override
public void remove(Component component) {
components.remove(component);
}
@Override
public Component getChild(int i) {
// 返回该节点下第i个子节点
return components.get(i);
}
@Override
public void doSomething() {
// 先打印本节点ID
System.out.println(nodeId);
// 接着这里用迭代器模式打印子节点ID
Iterator iterator = components.iterator();
while (iterator.hasNext()) {
Component component = (Component) iterator.next();
// 递归调用,如果节点下面还有节点,会开始另一个节点的打印
component.doSomething();
}
}
}
最后,节点之间的连接在后面的测试里进行。
组件模式测试
public class Main {
public static void main(String[] args) {
// 创建6个节点的树
final int NODE_NUM = 6;
Component[] nodes = new Component[NODE_NUM];
for (int i = 0; i < NODE_NUM; i++) {
nodes[i] = new Node(i);
}
// 节点组合连接
nodes[0].add(nodes[1]);
nodes[0].add(nodes[2]);
nodes[0].add(nodes[3]);
nodes[2].add(nodes[4]);
nodes[2].add(nodes[5]);
// 因为在doSomething()中是先打印节点ID,所以这里显示的结果
// 是先序遍历打印,以0为根的树的先序顺序:根 - 左 - 右,
//
// 如果把节点打印,放在doSomeThing()最后打印,
// 则为后序顺序遍历:左 - 右 - 根。
nodes[0].doSomething();
// nodes[0]的子节点有三个,其中索引为1的子节点就是nodes[2]
System.out.println(nodes[0].getChild(1).equals(nodes[2]));
// nodes[2]的子节点有两个,移除nodes[4],剩下nodes[5]
nodes[2].remove(nodes[4]);
nodes[2].doSomething();
}
}
测试结果
0
1
2
4
5
3
true
2
5