本文主要是看了《设计模式》做的笔记和思考,在此分享仅代表个人观点,如有不对的地方欢迎批评和指正。
1. 基础
Composite(组合)模式主要是解决嵌套结构中对象之间的关系,比如文件和文件夹,这两者在大多数情况下还具有很多共同点。Composite模式的UML图如下(照着我书上画的)。
稍微解释一下:
- Component 提供一个统一Leaf和Composite的抽象,里面有通用的接口。
- Leaf 叶子节点,相当于文件系统中的文件,没有子节点。
- Composite 类似容器的概念,相当于文件系统中的文件夹,可能有子节点。
- Client 调用者
这里重点是Component的统一抽象,正是因为这个才能统一Leaf和Composite的接口,因此Component中要尽可能地定义通用接口,以满足Leaf和Composite的需要。
1.1 好处
- 这个模式定义了基本对象和组合对象,组合对象也可以跟其他组合对象组合成更复杂的对象;
- 能够方便地设计基本对象和组合对象的共同属性和行为;
- Leaf和Composite的子类可以方便地接入原有的体系;
- 可以通过递归方便地遍历整个结构
1.2 部分实现
在Component中,方法都应该设置为省缺实现,便于照顾叶子节点,比如其中的add操作。这样Leaf就可以不再重写add方法,缩减代码量。
class Component{
void add(Component cpn){
return;
}
}
为了保存子节点,Composite需要用到一些存储结构,比如列表、树等等,主要是看性能。
class Composite extends Component{
private ArrayList<Component> list;
@Override
void add(Component cpn){
list.add(cpn);
}
// remove类似
@Override
Component getChild(int pos){
return list.get(pos);
}
}
2. 其他可选设计
2.1 显式的父节点引用
为了方便查找和遍历,可以显式地引入父节点的引用,比如这样:
class Component{
private Component father;
protected void setFather(Component father){
this.father = father;
}
public Component getFather(){
if(father == null){
throw new Exception("没有父节点");
}
}
}
class Composite extends Component{
private ArrayList<Component> list;
@Override
void add(Component cpn){
cpn.setFather(this);
list.add(cpn);
}
}
2.2 子节点排序
只有排序后才能更快地进行查询,你可以根据自己使用的语言编写相应代码,比如在Java中实现Comparable接口即可在加入List时自动排序。
2.3 缓存子节点
为了应对频繁的检索,有时可以设置一个缓存,避免了多次遍历,提高效率,比如当你存储子节点的结构是树或者链表。
class Composite extends Component{
private HashMap<Integer,Component> map;
private static int idHolder;
private static Component cpnHolder;
@Override
Component getChild(int id){
if(id != idHolder && cpnHolder == null){
idHolder = id;
cpnHolder = map.get(id);
}
return cpnHolder;
}
}
不过需要注意的是,当子节点发生变化时也需要更新缓存,保证其正确性。
3. 总结
该模式很好地处理了嵌套结构中,组合对象身份多样的情况,并且提供了统一的抽象来简化代码和使用,实际的例子有Android中View与ViewGroup,有相关编程经验的读者应该深有体会。