设计模式学习笔记十七:组合模式(Composite Pattern)

     1.概述

     组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

     组合模式(Composite Pattern):将对象组合成树形结构以表示‘部分-整体’的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

     组合模式结构图如下:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt

     结构图说明:

     (1)Component:组合中的对象声明接口,在适当情况下实现所有类共有的默认行为,声明一个接口用于访问和管理Component的子组件。在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。(可选)

     (2)Leaf:在组合中表示叶节点,叶节点没有子节点,定义对象的基本行为。

     (3)Composite:定义有子部件的那些部件的行为,存储子部件并在Component接口实现与子部件有关的操作。

     (4)Client:通过Component接口操作组合部件的对象。

     组合模式基本代码:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02
     public   abstract   class  Component
    {
        
protected   string  name;

        
public  Component( string  name)
        {
            
this .name  =  name;
        }

        
public   abstract   void  Add(Component c);
        
public   abstract   void  Remove(Component c);
        
public   abstract   void  Display( int  depth);
    }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02

     Composite:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02
    public   class  Composite : Component
    {
        
private  List < Component >  children  =   new  List < Component > ();

        
public  Composite( string  name)
            : 
base (name)
        { }

        
public   override   void  Add(Component c)
        {
            children.Add(c);
        }

        
public   override   void  Remove(Component c)
        {
            children.Remove(c);
        }

        
public   override   void  Display( int  depth)
        {
            Console.WriteLine(
new  String( ' - ' , depth)  +  name);

            
foreach  (Component component  in  children)
            {
                component.Display(depth 
+   2 );
            }
        }
    }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02

     Leaf:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02
     public   class  Leaf : Component
    {
        
public  Leaf( string  name)
            : 
base (name)
        { }

        
public   override   void  Add(Component c)
        {
            Console.WriteLine(
" Cannot add to a leaf " );
        }

        
public   override   void  Remove(Component c)
        {
            Console.WriteLine(
" Cannot remove from a leaf " );
        }

        
public   override   void  Display( int  depth)
        {
            Console.WriteLine(
new  String( ' - ' , depth)  +  name);
        }
    }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02

     客户端:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02
     public   class  Program
    {
        
static   void  Main( string [] args)
        {
            Composite root 
=   new  Composite( " root " );
            root.Add(
new  Leaf( " Leaf A " ));
            root.Add(
new  Leaf( " Leaf B " ));

            Composite comp 
=   new  Composite( " Composite X " );
            comp.Add(
new  Leaf( " Leaf XA " ));
            comp.Add(
new  Leaf( " Leaf XB " ));

            root.Add(comp);

            Composite comp2 
=   new  Composite( " Composite XY " );
            comp2.Add(
new  Leaf( " Leaf XYA " ));
            comp2.Add(
new  Leaf( " Leaf XYB " ));

            comp.Add(comp2);

            root.Add(
new  Leaf( " Leaf C " ));

            Leaf leaf 
=   new  Leaf( " Leaf D " );
            root.Add(leaf);
            root.Remove(leaf);

            root.Display(
1 );

            Console.Read();
        }
    }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_02

     可以看出,Composite类型的对象可以包含其它Component类型的对象。换而言之,Composite类型对象可以含有其它的树枝(Composite)类型或树叶(Leaf)类型的对象。

     组合模式的实现根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(诸如:add、remove等)。安全式的组合模式要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件中。与安全式的组合模式不同的是,透明式的组合模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定的接口。

     透明模式:也就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具有完全一致的行为接口,但问题也很明显,因为Leaf类本身不具备Add(),Remove()方法的功能,所以实现他是没有意义的。

     安全模式:就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样就不会出现透明模式出现的问题,不过由于不够透明,所以叶节点和枝节点将不具有相同的接口,客户端调用需要做相应的判断,带来了不便。

     2.实例(大话设计模式)

     大话设计模式中的公司管理系统的结构图如下:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_10

     具体实现代码如下:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_11
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12    public abstract class Company
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_13    
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
protected string name;
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public Company(string name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            
this.name = name;
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public abstract void Add(Company c);//增加
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
        public abstract void Remove(Company c);//移除
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
        public abstract void Display(int depth);//显示
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
        public abstract void LineOfDuty();//履行职责
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_26    }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12    
public class ConcreteCompany : Company
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_13    
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
private List<Company> children = new List<Company>();
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public ConcreteCompany(string name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            : 
base(name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{ }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Add(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            children.Add(c);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Remove(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            children.Remove(c);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Display(int depth)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            Console.WriteLine(
new String('-', depth) + name);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            
foreach (Company component in children)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17            
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14                component.Display(depth 
+ 2);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19            }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
//履行职责
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
        public override void LineOfDuty()
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            
foreach (Company component in children)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17            
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14                component.LineOfDuty();
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19            }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_26    }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12    
//人力资源部
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12
    public class HRDepartment : Company
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_13    
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public HRDepartment(string name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            : 
base(name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{ }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Add(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Remove(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Display(int depth)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            Console.WriteLine(
new String('-', depth) + name);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void LineOfDuty()
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            Console.WriteLine(
"{0} 员工招聘培训管理", name);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_26    }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12    
//财务部
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_12
    public class FinanceDepartment : Company
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_13    
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public FinanceDepartment(string name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            : 
base(name)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{ }
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Add(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Remove(Company c)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void Display(int depth)
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            Console.WriteLine(
new String('-', depth) + name);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14        
public override void LineOfDuty()
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_17        
{
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14            Console.WriteLine(
"{0} 公司财务收支管理", name);
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_19        }

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_14
设计模式学习笔记十七:组合模式(Composite Pattern)_qt_26    }

     客户端代码:

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_11
    public class Program
    {
        
static void Main(string[] args)
        {
            ConcreteCompany root 
= new ConcreteCompany("北京总公司");
            root.Add(
new HRDepartment("总公司人力资源部"));
            root.Add(
new FinanceDepartment("总公司财务部"));

            ConcreteCompany comp 
= new ConcreteCompany("上海华东分公司");
            comp.Add(
new HRDepartment("华东分公司人力资源部"));
            comp.Add(
new FinanceDepartment("华东分公司财务部"));
            root.Add(comp);

            ConcreteCompany comp1 
= new ConcreteCompany("南京办事处");
            comp1.Add(
new HRDepartment("南京办事处人力资源部"));
            comp1.Add(
new FinanceDepartment("南京办事处财务部"));
            comp.Add(comp1);

            ConcreteCompany comp2 
= new ConcreteCompany("杭州办事处");
            comp2.Add(
new HRDepartment("杭州办事处人力资源部"));
            comp2.Add(
new FinanceDepartment("杭州办事处财务部"));
            comp.Add(comp2);


            Console.WriteLine(
"\n结构图:");

            root.Display(
1);

            Console.WriteLine(
"\n职责:");

            root.LineOfDuty();


            Console.Read();
        }
    }

     在.NET中,一个典型的组合模式实例就是.NET的控件,如Button,TextBox和Label等,这些控件都是继承自Control类,该类自身包含ControlCollection的集合Controls,控件和子控件的逻辑关系如下图:

 

设计模式学习笔记十七:组合模式(Composite Pattern)_qt_121
      3.总结

     何时采用组合模式:

     1.需求重要体现部分与整体的层次结构时

     2.你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象

     使用效果:

     1.Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
     2.将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。
     3.Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。
     4.Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

     参考资料:

      大化设计模式

     http://terrylee.cnblogs.com/archive/2006/03/11/347919.html