组合模式的应用场景主要是有树形结构出现的,例如在《大话设计模式》这本书中提出的这种场景:
如上图,有某一个大公司,总部在北京,在上海设有分公司,又在南京和杭州设有办事处,每一个地方都有人力资源部和财务部两个部门。现在在北京总部使用着一套OA系统,高层觉得这套系统不错,要推广给下属公司,于是就给OA软件的设计厂商提出了要求。
如果程序要一级一级判断这是总公司、分公司还是办事处就太麻烦了,要是哪一天再开分公司或办事处怎么办呢?如果要为每一处独立开发会造成大量的代码冗余。最好的办法就是代码的共享和复用,观察上图不难发现:办事处、分公司和总部之间是部分与整体的关系,可以把它们看作整体来对待,这样总部的人力资源部和财务部的管理代码就可复用到其他下属公司,就好比在处理Word字体颜色的时候可以单个字符处理也可以整篇文章处理。
那要怎么进行代码复用?如何设计这种部分与整体统一看待的模式呢?于是就要用到组合模式:
组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。——《大话设计模式》
如果按照定义把上面的公司组织图看作一棵树的话,树根是北京总公司,叶子节点是人力资源部和财务部,它们不可再向外派生。非叶节点(不含树根)就是上海分公司、南京办事处和杭州办事处。也就是说北京总公司、上海分公司、南京办事处是组合对象,可以继续派生。
组合模式的UML类图是这样的:
这个Component是一个抽象类,里面规定了组合对象和叶子节点的可用方法,由它来派生出两个类,一个是叶子节点类Leaf,另外一个是组合对象类Composite,叶子节点只实现Component抽象类中的部分抽象方法,因为其他实现了并没有意义,虽然在实际实现中还是要给出方法体(比如人力资源部是不可以增加和删除部门的);Composite类实现所有方法。Composite与Component是聚合关系,也就是说Composite是Component的一部分。
下面再举一个更简单的例子吧~比如一棵树,有树叶和树枝(这里假设树根也是一种树枝),那么枝外可生叶,亦可生枝;但叶外不可生枝和叶。
我们不妨将树叶与树枝的例子模拟成代码,利用组合模式输出这棵树的组织结构:
import java.util.ArrayList;
import java.util.List;
//树叶和数值统一的接口
abstract class Component {
public abstract void add(Component component);
public abstract void remove(Component component);
public abstract void display(int deepth);
}
//树叶 叶子节点 不可再扩展
class Leaf extends Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void display(int deepth) {
for (int i = 0; i < deepth; i++) {
System.out.print("-");
}
System.out.println(this.name);
}
@Override
public void add(Component component) {
// TODO Auto-generated method stub
}
@Override
public void remove(Component component) {
// TODO Auto-generated method stub
}
}
//树枝 可扩展
class Branch extends Component {
private String name;
private List<Component> component;
public Branch(String name) {
this.name = name;
this.component = new ArrayList<Component>();
}
@Override
public void add(Component component) {
// TODO Auto-generated method stub
this.component.add(component);
}
@Override
public void remove(Component component) {
// TODO Auto-generated method stub
this.component.remove(component);
}
@Override
public void display(int deepth) {
for (int i = 0; i < deepth; i++) {
System.out.print("-");
}
System.out.println(this.name);
//对下属进行遍历,深度+2
for(Component c : component)
{
c.display(deepth+2);
}
}
}
public class Main {
public static void main(String[] args) {
Branch root = new Branch("树根");
Branch branch1 = new Branch("树枝A");
Branch branch2 = new Branch("树枝B");
Leaf leaf1 = new Leaf("树叶A");
Leaf leaf2 = new Leaf("树叶B");
Leaf leaf3 = new Leaf("树叶C");
//树干上可伸出树枝A
root.add(branch1);
//树干上同样可长出树叶A
root.add(leaf1);
//树枝A上可以长出树叶B
branch1.add(leaf2);
//树枝A上亦可长出树枝B
branch1.add(branch2);
//树枝B上可以长出树叶C
branch2.add(leaf3);
root.display(1);
/*
* 结构图
*
* 树根
* / \
* 树叶A 树枝A
* / \
* 树叶B 树枝B
* /
* 树叶C
*
*/
}
}
运行结果如下:
-树根
---树枝A
-----树叶B
-----树枝B
-------树叶C
---树叶A
可以发现,程序输出的结构和代码中的组织图是一致的~