一、composite能做什么?
1.composite定义了一个tree-like的part-whole的层级关系。
2.这个体系中的类都会实现统一的接口,这样客户端就可以通过一致的方式访问所有的对象。那些复合对象(包含子对象)增加或删除子对象时,客户端不用改变调用代码(客户端可以完全不用关心这些事情)。
3.结合iterater,调用方可以透明的从树的根开始遍历完这个体系中的所有对象,即客户仅需要在根上调用某个方法,这个体系中的所有对象的这个方法都会被调用。
二、composite是什么?
说了这么多composite的好处,那composite到底是什么呢?首先来看看它的定义:
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.

这里有几个很关键的角色:
1.IComponent:我们的属性体系都要实现的接口。以保证能用同样的方式调用体系中的对象,而不用担心对象是CompositeComponent或LeafComponent。
2.CompositeComponent:这类对象含有子对象,而子对象可以是LeafComponent,也可以是CompositeComponent(如此递归下去)。
3.LeafComponent: 这类对象不包含子对象(想想递归,通常这类对象就是递归的终结条件)。
问题:
1.因为LeafComponent和 CompositeComponent实现了统一的接口,这有可能带来彼此会包含并不适合自己的方法,而当客户端调用了这些方法时该怎么办?
这个问题让人很纠结。唯有具体情况具体分析了。常用的解决办法有:
a.在实现不适合自己的方法时抛出未实现或者不支持异常。由此而来的问题是在使用这些方法时,不得不加try,catch代码(尽管这并不是business的一部分)。
b.添加默认实现。比如对于LeafComponent实现Add时,不做任何事情就可以了。但是对于LeafComponent调用Add方法,成功执行了但没有任何效果总让人觉得不自在。
2.IComponent抽象了LeafComponent和CompositeComponent的行为,不是违反单一职责原则吗?
从某种层面说:是的。但是这也带来了使用方不用关心对象究竟是Leaf还是Composite的好处。而这也恰恰是该模式的一个主要关注点。
当然我们也可以将Leaf和Composite的行为抽象到不同的接口当中,但在使用时就不得不面临要obj is ILeaf这样的代码了。
总之,凡是都有两面性,模式也如此,没有放之四海皆准的标准。具体怎么抉择还得看‘国情’。
三、身边的composite
对于asp.net开发人员来说,control或者page就是我们每天都会面对的composite。
一个 WebForm就是一棵控件树。这棵控件树包含中的对象都直接或间接继承于Control。在页面的生命周期中,很多阶段都是调用了Page的相关方法后,framework会递归地调用Page的子控件的相应方法(如此递归下去,直至所有控件的相应方法调用完毕)。但是这个递归的过程并不是通过迭代器,而是在Control内部完成的。下面提供一个结合迭代器来遍历的简单实现:
public class CLSEnum : IEnumerator
{
private ControlCollection cls = null;
private Stack<IEnumerator> enums = new Stack<IEnumerator>();
public CLSEnum( ControlCollection cls)
{
this.cls = cls;
enums.Push(cls.GetEnumerator());
}
#region IEnumerator Members
public object Current
{
get
{
var iter = enums.Peek();
var curObj = iter.Current as Control;
if( curObj != null)
{
if( curObj.HasControls())
{
enums.Push(curObj.Controls.GetEnumerator());
}
}
return curObj;
}
}
public bool MoveNext()
{
if( enums.Count == 0)
{
return false;
}
else
{
while (enums.Count > 0)
{
var iter = enums.Peek();
if( iter.MoveNext())
{
return true;
}
else
{
enums.Pop();
}
}
return false;
}
}
public void Reset()
{
enums.Clear();
enums.Push(cls.GetEnumerator());
}
#endregion
}
四、小结
在遇到树形结构时都可以考虑使用composite模式。 具体的实现形式则因情况而异。