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();

      

    }

}

    难点一:现在虽然看上去已经好像解决满足实现,但是AddRemove方法还需要判断IBox类型,如果是SingleBox就不存在AddRemove方法。外部使用的时候它不知道是不是容器对象,它怎么添加呢

    难点二:ContainerBox里包含了一堆的Box,既包括ContainerBox,又包括SingleBox,有一种树型的感觉,一个对应树型的叶子,一部分对应树型的树叉

    难点三:虽然在接口里声明了AddRemove方法,看似有些不合理,但是对应的对客户代码就比较透明了。方法还是可取的

 

Composite模式的几个要点

l      Composite模式采用树形结构来实现普遍存在的对象窗口,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。

l      将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口而非对象容器的复内部实现结构发生依赖关系,从而更能“应对变化”。

l      Composite模式中,是将“AddRemove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.Net控件的实现在这方面为我们提供了一个很好的示范。

l      Composite模式在具体实现中,可以让父对象中的子对象反射追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

源自:李建忠老师

如果改用容器对象的设计,底下的代码如ASP.Net运用PanelControls一样,请看:

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.

public interface IBox

{

    
void Process();

    IList Boxes
{get;set;};//添加属性,实现类中就不需要add,remove了

}




public class SingleBox : IBox

{

    
public void Process()

    
{

       
//1.Do process for myself

       
//.

    }


}


public class ContainerBox : IBox

{

    
//1.Do process for myself

    
//.



    
//2.Do process for the box in the list

    
if(this.Boxes != null)

    
{

       
foreach (IBox box in this.Boxes)

       
{}

    }


}

 

 

compositedisplay/hide example sentences
KK: []
DJ: []
a.
1. 合成的,复合的,混成的
a composite picture by all the children in the class
一幅由全班孩子共同绘成的画
2. 【建】(大写)(柱型)混合的
3. 【植】菊科的
n.[C]
1. 合成物;复合材料
English is a composite of many languages.
英语是多种语言混合而成的。
2. 菊科植物
posted on 2009-01-17 16:26  阿C's  阅读(290)  评论(0)    收藏  举报