设计模式系列漫谈之八 - 复合模式

故事
      10月20日, 我们公司一行几人参加绍兴客户的项目招标。出乎意料的是,就在当天,这家单位同时举行三个项目的招标(真牛!),所以十几家单位都在等候。我们上午8:30就到了现场,而我们投标的项目去排在下午进行。傻坐了一会,我们提议到鲁迅故居去看看。其实几次去绍兴出差都匆匆忙忙的,今天终于如愿以偿。

      近来确实锻炼太少,几个小时下来,我的脚又酸又痛,已经累得走不动了。不过,其中两幅图给我深刻的印象。一幅是三维立体效果的绍兴古城图,讲述着许多动人的故事;另一幅是周氏家族图,象一棵参天大树一样,枝枝蔓蔓。可以看出,鲁迅家族是浙江绍兴会稽县的一个大家族。鲁迅,原名周树人,他还有两个兄弟(周建人和周作人),他的父亲叫周伯宜,他有个儿子叫周海婴······,不说不了,再说就快晕了。

      我在想一个可笑的问题,在整个周氏家族里,谁最能生儿子啊? 当然,我们没有这么多时间从头至尾数一遍。那么,就让我们用程序来解决吧。


复合模式(Composite)的解决方案
         复合模式又叫部分-整体模式(Part-Whole)。将对象组织成树型结构(周氏家族图),可以用来描述整体与部分的关系,使客户端将单纯元素与复合元素同等看待,以此遍历树结构所有元素(所有周氏人)。复合模式需要定义三个对象:
      抽象接口(IElement):这是一个抽象类/接口,它给所有对象定义接口,提供操作行为,如StatSons方法统计有多少个儿子。 
      组合(Composite)对象:代表包含子元素的组合。 
      元素(Element)对象:代表子元素对象。一个子元素没有下级子对象。

抽象接口如下:

namespace LuFamily
{
       
public interface IElement
        {
               
string Name{getset;}
               
void Add(IElement t);
               
void Remove(IElement t);
               
void StatSons();
         }
}

组合对象如下:

namespace LuFamily
{
       
public class Composite:IElement
        {
               
private List<IElement> elementList = new List<IElement>();

               
private string _Name;
               
public string Name
               {
                     
get{return _Name;}
                     
set{_Name=value;}
               }
               
public void Add(IElement t)
               {
                     elementList.Add(t);
               }
               
void Remove(IElement t)
               {
                     elementList.Remove(t);
               }
               
void StatSons()
               {
                     Console.WriteLine(elementList.Count);
                     
                     
foreach(IElement element in elementList)
                           element.StatSons();
               }
         }
}

元素对象如下:

namespace LuFamily
{
       
public class Element:IElement
        {
               
private string _Name;
               
public string Name
               {
                     
get{return _Name;}
                     
set{_Name=value;}
               }
               
public void Add(IElement t)
               {
                     ;
               }
               
void Remove(IElement t)
               {
                     ;
               }
               
void StatSons()
               {
                     Console.WriteLine(
0);
               }
         }
}

客户端调用如下:

Composite root=new Composite();
root.Name
="周伯宜";

Element e1
=new Element();
e1.Name
="周建人";
root.Add(e1);

Element e2
=new Element();
e2.Name
="周作人";
root.Add(e1);

Composite c1
=new Composite();
c1.Name
="周树人";
root.Add(c1);

Element e3
=new Element();
e3.Name
="周海婴";
c1.Add(e3);

root.StatSons();


      也许您会问,既然元素对象(Element)没有下级子对象,为什么还要实现Add和Remove方法呢?其实主要是为了实现客户端对元素对象与组合对象统一对待,而不必区分哪个是元素对象,哪个是组合对象,这是复合模式两种形式其中之一,即透明复合模式。采用这种方式的缺点是不够安全,因为在实际应用中,元素对象与组合对象存在着本质区别。

      复合模式的另一种形式自然是安全复合模式。采用安全复合模式就是把元素对象与组合对象的在本质上区分开来。即元素对象不需要提供Add和Remove等元素集合的管理方法。让我们来看看安全复合模式的实现方法。

抽象接口如下:

namespace LuFamily
{
       
public interface IElement
        {
               
string Name{getset;}
               
void StatSons();
         }
}

组合对象如下:

namespace LuFamily
{
       
public class Composite:IElement
        {
               
private List<IElement> elementList = new List<IElement>();

               
private string _Name;
               
public string Name
               {
                     
get{return _Name;}
                     
set{_Name=value;}
               }
               
public void Add(IElement t)
               {
                     elementList.Add(t);
               }
               
void Remove(IElement t)
               {
                     elementList.Remove(t);
               }
               
void StatSons()
               {
                     Console.WriteLine(elementList.Count);
                     
                     
foreach(IElement element in elementList)
                           element.StatSons();
               }
         }
}

元素对象如下:

namespace LuFamily
{
       
public class Element:IElement
        {
               
private string _Name;
               
public string Name
               {
                     
get{return _Name;}
                     
set{_Name=value;}
               }
               
void StatSons()
               {
                     Console.WriteLine(
0);
               }
         }
}

客户端调用如下:

Composite root=new Composite();
root.Name
="周伯宜";

Element e1
=new Element();
e1.Name
="周建人";
if (root is Composite)
   root.Add(e1);

Element e2
=new Element();
e2.Name
="周作人";
if (root is Composite)
   root.Add(e1);

Composite c1
=new Composite();
c1.Name
="周树人";
if (root is Composite)
   root.Add(c1);

Element ce1
=new Element();
ce1.Name
="周海婴";
if (c1 is Composite)
   c1.Add(ce1);

root.StatSons();

为了安全起见,执行Add方法的时候,最好判断是否为组合对象。

posted @ 2007-12-21 21:56  李华星  阅读(1876)  评论(0编辑  收藏  举报