1. 组合模式的概念
组合模式(Composite), 将对象组合成属性结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式也被成为 部分-整体模式,属于结构型的设计模式。
组合模式比较简单,它将一组相似的对象看做一个对象来处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别。
生活中一个比较典型的例子就是公司的组织结构树状图,如下所示:
上面是一个公司的组织结构树状图,其中总公司下有人力资源部与财务部,而且总公司下属还有一个分公司,分公司有自己的人力部和财务部,对于总公司来说,子公司就是一个独立的个体,与总公司所属的人力部和财务部是平级的。
糟糕的使用:
当我们在构建后台系统时,如果我们要让上图中所有的部门都复用同一套代码,这个时候就会产生因为大量的判断语句,我首先要判断是否为总公司的人力部、再判断是否为总公司的财务部、再判断是否为子公司的人力部、再判断是否为子公司的财务部,当部门一多或或者共用的代码一多,就会显得十分臃肿,产生了非常糟糕的代码。
抽象结构:
在这么一个结构中,大家可以看到,虽然总公司和子公司本质不一样,但是它在我们的组织结构中是一样的,我们可以把他们看做是一个抽象的公司,在组合模式中我们将这样一个拥有分支的节点成为枝干构件,位于树状结构顶部的枝干结构比较特殊,我们成为根结构件,因为其为整个树状图的始端,同样对于像人力部和财务部这样没有分支的结构,我们则成为 叶子构件,这样的结构就是组合模式的雏形:
2. UML图
来看下组合模式的UML类图:
它有这么几个成员角色:
Component
,抽象根节点
为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。
声明一个接口用于访问和管理Component的子节点。可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它Composite
定义有子节点的那些枝干节点的行为,存储子节点,在Component接口中实现与子节点有关的操作Leaf
在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点的对象行为
下面展示一下模板代码,
先来看下抽象根节点:
public abstract class Component {
// 节点名
protected String name;
public Component(String name) {
this.name = name;
}
/**
* 具体的逻辑方法由子类实现
*/
public abstract void doSomething();
}
来看下具体实现抽象根节点的具体枝干节点:
public class Composite extends Component {
/**
* 存储节点的容器
*/
private List<Component> components = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.println(name);
if (components != null) {
for (Component c : components) {
c.doSomething();
}
}
}
/**
* 添加子节点
*
* @param child
*/
public void addChild(Component child) {
components.add(child);
}
/**
* 移除子节点
*
* @param child
*/
public void removeChild(Component child) {
components.remove(child);
}
/**
* 获取子节点 根据对应的下标
*
* @param index
* @return
*/
public Component getChildren(int index) {
return components.get(index);
}
}
接下来是叶子节点,它也继承了Component类:
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void doSomething() {
System.out.println(name);
}
}
最后来看下客户端类是如何应用组合模式的:
class CompositeMain {
public static void main(String[] args) {
// 构造一个根节点
Composite root = new Composite("Root");
// 构造两个枝干节点
Composite branch1 = new Composite("Branch1");
Composite branch2 = new Composite("Branch2");
// 构造两个叶子节点
Leaf leaf1 = new Leaf("Leaf1");
Leaf leaf2 = new Leaf("Leaf2");
//将叶子节点添加至枝干节点中
branch1.addChild(leaf1);
branch2.addChild(leaf2);
//将枝干节点添加到跟节点中
root.addChild(branch1);
root.addChild(branch2);
//执行方法
root.doSomething();
}
}
3. 组合模式在Android中应用
在Android中,组合模式是有非常经典的应用的,而且天天都会用到,那就是View和ViewGroup的嵌套组合。
我们来看下View和ViewGroup的关系:
这里省略了View和ViewGroup类中的一些方法,在Android的这个视图层级中,容器一定是ViewGroup,而且只有ViewGroup才能包含其他的View,比如 LinearLayout能包含TextView、Button、CheckBox等,但是反过来TextView是不能包含LinearLayout的,因为TextView直接继承与View,其并非一个容器。这里View的视图层级中使用到的其实是一种安全的设计模式。
具体的源码这里就不做分析了。
4. 小结
组合模式的使用场景:
- 表示对象可以形成部分-整体层次结构时。
- 从一个整体中能够独立处部分模块或功能的场景(显然,View这个模块能够独立出ViewGroup这个模块出来)
组合模式让客户可以一致地使用组合结构和单个对象。