Composite模式
对象容器的问题
在面向对象系统中,我们常会遇到一类具有“容器”特征的对象—即它们在充当对象的同时,又是其他对象的容器。(如俄罗斯套娃)
public class SingleBox : Ibox {
public void process(){ … }
}
public class ContainerBox : Ibox {
public void process() { … }
public ArrayList getBoxes() { … } //主要是解决这个getBoxes.
}
如果我们要对这样的对象容器进行处理:
Ibox box = Factory.GetBox();
if (box is ContainerBox) {
box.process();
ArrayList list = ((ContainerBox)box).GetBoxes();
……//将面临比较复杂的递归处理
}else if (box is SingleBox){
box.process();
}
意图(Intent)
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
--《设计模式》GoF
public interface IBox
{
void Process();
void Add(IBox box);
void Remove(IBox box);
}
public class SingleBox : IBox
{
public void Process()
{
//1.Do process for myself
//....
}
public void Add(IBox box)
{
//如果什么都不做给用户不好的体验,客户点了什么都没响应
throw UnsupporttedException();
}
public void Remove(IBox box)
{
throw UnsupporttedException();
}
}
public class ContainerBox : IBox
{
ArrayList list = null;
public void Add(IBox box)
{
if(list == null)
{
list = new ArrayList();
}
list.Add(box);
}
public void Remove(IBox box)
{
if(list == null)
{
throw new XXXException();
}
list.Remove(box);
}
public void Process()
{
//1.Do process for myself
//....
//2.Do process for this box in the list
if(list != null)
{
foreach (IBox box in list)
{
box.Process(); //这个时候,在内部实现了递归。这个box,不管你是单个的box,还是一个容器的对象,无所谓,都可以去调用
//Process方法,如果它是一个ContainerBox它继续调用这个结构,如果是单个
//对象,那它就直接做SingleBox里的Process。
}
}
}
}
//客户代码
public class App
{
public static void Main()
{
IBox box = Factory.GetBox();
//客户代码与抽象接口进行耦合
box.Process();
}
}
难点一:现在虽然看上去已经好像解决满足实现,但是Add,Remove方法还需要判断IBox类型,如果是SingleBox就不存在Add,Remove方法。外部使用的时候它不知道是不是容器对象,它怎么添加呢
难点二:ContainerBox里包含了一堆的Box,既包括ContainerBox,又包括SingleBox,有一种树型的感觉,一个对应树型的叶子,一部分对应树型的树叉
难点三:虽然在接口里声明了Add,Remove方法,看似有些不合理,但是对应的对客户代码就比较透明了。方法还是可取的
Composite模式的几个要点
l Composite模式采用树形结构来实现普遍存在的对象窗口,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
l 将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口—而非对象容器的复内部实现结构—发生依赖关系,从而更能“应对变化”。
l Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.Net控件的实现在这方面为我们提供了一个很好的示范。
l Composite模式在具体实现中,可以让父对象中的子对象反射追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。
源自:李建忠老师
如果改用容器对象的设计,底下的代码如ASP.Net运用Panel的Controls一样,请看:
Button b = new Button();
Panel p = new Panel();
p.Controls.Add(b);
Panel p2 = new Panel();
TextBox t = new TextBox();
p.Controls.Add(t);
p.Controls.Add(p);
所有的ASP.Net的控件都是容器控件,除了LiteralControl控件它是原子控件,因为其他控件就算Button都可以包含LiteralControl.











































KK: [
DJ: [