场景:公司最近接了一个项目,为一家大公司做一个工作管理系统,类似于常见的OA系统,单从需求分析的话,不难开发。但是考虑到大公司希望在所有分公司推广使用,如果采用简单复制同一套代码,效果不如人意,总公司和分公司是成树状结构,也就是有组织结构的,不能是简单的平行管理。有些功能只能总公司使用,而部分功能总公司和分公司可以共同使用,比如人力资源部之类的。这就涉及到整体与部分的关系—整体和部分可以被一致对待的问题!
由此引出新的设计模式
组合模式(Composite):将对象组合成树状结构以表示'部分-整体'的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式(Composite)结构图
Component为组合中的对象抽象类,在适当情况下,实现所有类的默认方法
package composite;
abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
/**
* @param c
* 通过用Add和Remove方法来提供增加或移除叶结点或枝结点的功能
*/
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract void Display(int depth);
}
Leaf在组合中表示叶节点对象,叶节点没有子节点
package composite;
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
/**
* 叶节点无法再增加分支和树叶,所以Add和Remove方法的实现没有意义,
* 但是这样做可以消除叶结点和枝结点在抽象层次的区别,他们具有完全一致的接口
*/
@Override
public void Add(Component c) {
System.out.println("Cannot add to a leaf ");
}
@Override
public void Remove(Component c) {
System.out.println("Cannot remove from a leaf");
}
/**
* @see composite.Component#Display(int)
* 叶结点的具体方法,此处是显示其名称和级别的
*/
@Override
public void Display(int depth) {
System.out.println(new StringBuffer("-").append(depth)+ name);
}
}
Composite定义枝节点的行为,用来存储子部件
package composite;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Composite extends Component {
//一个子对象的集合用了存储其下属的枝结点和叶节点
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void Add(Component c) { //增加下级结点
children.add(c);
}
@Override
public void Remove(Component c) { //移除下级结点
children.remove(c);
}
/**
* 显示枝结点名称,并对其下级进行遍历
*/
@Override
public void Display(int depth) {
System.out.println(new StringBuffer("-").append(depth)+name);
Iterator<Component> iterator = children.iterator();
while(iterator.hasNext()){
iterator.next().Display(depth+2);
}
}
}
客户端代码,通过Component操作组合部件的对象
package composite;
public class Client {
public static void main(String[] args) {
/**
* 树根root,其下有两片子叶 A B
*/
Composite root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
/**
* 树根root,长出分支M,分支下有两片子叶 X ,Y
*/
Composite M = new Composite("Composite M");
M.Add(new Leaf("Leaf X") );
M.Add(new Leaf("Leaf Y") );
root.Add(M);
/**
* 树根root,长出分支N,分支下有两片子叶1 ,2
*/
Composite N = new Composite("Composite N");
N.Add(new Leaf("Leaf 1") );
N.Add(new Leaf("Leaf 2") );
root.Add(N);
//显示树的结构
root.Display(1);
}
}
结果显示
-1root
-3Leaf A
-3Leaf B
-3Composite M
-5Leaf X
-5Leaf Y
-3Composite N
-5Leaf 1
-5Leaf 2
树有无数的分支,可以通过Composite来实现树状结构,而叶节点Leaf不能再增加分支,所以Componet内声明的Add和Remove方法对于Leaf来讲实现是没有意义的。这种方式叫做透明方式,就是说Componet中声明所以用来管理子对象的方法,包括Add和Remove,实现Componet的所有子类都具备了Add和Remove。这样做的 好处就是叶节点和枝结点对于外界没有区别,具备完全一致的行为方法。但是问题也很明显,Leaf内的Add和Remove方法是没意义的
如果不希望Leaf内有无用的方法,可以采用安全方式,也就是在Componet中不声明Add和Remove方法,子类Leaf就不需要实现它,而是在Composite声明管理子类对象的方法,这样就不会出现刚刚出现的问题,但是由于不够透明,叶节点和枝结点将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便
何时使用组合模式:需求中体现部分和整体层次的结构时,以及希望用户可以忽略组合对象和单个对象的不同,统一地使用组合结构中的所以对象时
组合模式的好处:定义的基本对象可以被组合成更复杂的组合对象,而组合对象又可以被组合,如此递归下去,客户端中,任何用到基本对象的地方都可以使用组合对象。用户不用去关系处理的是叶节点还是组合组件。
组合模式可以让客户一致地使用组合结构和单个对象