zoukankan      html  css  js  c++  java
  • [设计模式] 8 组合模式 Composite

    DP书上给出的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。注意两个字“树形”。这种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。

          下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。

           相应的代码实现为:

    class Company  
    {
    public:
        Company(string name) { m_name = name; }
        virtual ~Company(){}
        virtual void Add(Company *pCom){}
        virtual void Show(int depth) {}
    protected:
        string m_name;
    };
    //具体公司
    class ConcreteCompany : public Company  
    {
    public:
        ConcreteCompany(string name): Company(name) {}
        virtual ~ConcreteCompany() {}
        void Add(Company *pCom) { m_listCompany.push_back(pCom); } //位于树的中间,可以增加子树
        void Show(int depth)
        {
            for(int i = 0;i < depth; i++)
                cout<<"-";
            cout<<m_name<<endl;
            list<Company *>::iterator iter=m_listCompany.begin();
            for(; iter != m_listCompany.end(); iter++) //显示下层结点
                (*iter)->Show(depth + 2);
        }
    private:
        list<Company *> m_listCompany;
    };
    //具体的部门,财务部
    class FinanceDepartment : public Company 
    {
    public:
        FinanceDepartment(string name):Company(name){}
        virtual ~FinanceDepartment() {}
        virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
        {
            for(int i = 0; i < depth; i++)
                cout<<"-";
            cout<<m_name<<endl;
        }
    };
    //具体的部门,人力资源部
    class HRDepartment :public Company  
    {
    public:
        HRDepartment(string name):Company(name){}
        virtual ~HRDepartment() {}
        virtual void Show(int depth) //只需显示,无限添加函数,因为已是叶结点
        {
            for(int i = 0; i < depth; i++)
                cout<<"-";
            cout<<m_name<<endl;
        }
    };
    
    int main()
    {
        Company *root = new ConcreteCompany("总公司");
        Company *leaf1=new FinanceDepartment("财务部");
        Company *leaf2=new HRDepartment("人力资源部");
        root->Add(leaf1);
        root->Add(leaf2);
    
        //分公司A
        Company *mid1 = new ConcreteCompany("分公司A");
        Company *leaf3=new FinanceDepartment("财务部");
        Company *leaf4=new HRDepartment("人力资源部");
        mid1->Add(leaf3);
        mid1->Add(leaf4);
        root->Add(mid1);
        //分公司B
        Company *mid2=new ConcreteCompany("分公司B");
        FinanceDepartment *leaf5=new FinanceDepartment("财务部");
        HRDepartment *leaf6=new HRDepartment("人力资源部");
        mid2->Add(leaf5);
        mid2->Add(leaf6);
        root->Add(mid2);
        root->Show(0);
    
        delete leaf1; delete leaf2;
        delete leaf3; delete leaf4;
        delete leaf5; delete leaf6;    
        delete mid1; delete mid2;
        delete root;
        return 0;
    }

            上面的实现方式有缺点,就是内存的释放不好,需要客户自己动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所有的指针都是存在ConcreteCompany类的链表中。C++的麻烦,没有垃圾回收机制。

    什么是组合模式?

    在GOF的《设计模式:可复用面向对象软件的基础》一书中对组合模式是这样说的:将对象组合成树形结构以表示“部分-整体”的层次结构。组合(Composite)模式使得用户对单个对象和组合对象的使用具有一致性。

    组合模式(Composite)将小对象组合成树形结构,使用户操作组合对象如同操作一个单个对象。组合模式定义了“部分-整体”的层次结构,基本对象可以被组合成更大的对象,而且这种操作是可重复的,不断重复下去就可以得到一个非常大的组合对象,但这些组合对象与基本对象拥有相同的接口,因而组合是透明的,用法完全一致。

    我们这样来简单的理解组合模式,组合模式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方便的完成这些元素或者内部对象的访问和操作。我们也可以把组合对象理解成一个容器,容器提供各种访问其内部对象或者元素的API,我们只需要使用这些方法就可以操作它了。

    Component:

    1. 为组合中的对象声明接口;
    2. 在适当的情况下,实现所有类共有接口的缺省行为;
    3. 声明一个接口用于访问和管理Component的子组件。

    Leaf:

    1. 在组合中表示叶节点对象,叶节点没有子节点;
    2. 在组合中定义叶节点的行为。

    Composite:

    1. 定义有子部件的那些部件的行为;
    2. 存储子部件。

    Client:

    通过Component接口操作组合部件的对象。

    /*
    ** FileName     : CompositePatternDemo
    ** Author       : Jelly Young
    ** Date         : 2013/12/09
    ** Description  : More information, please go to http://www.jellythink.com
    */
    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    // 抽象的部件类描述将来所有部件共有的行为
    class Component
    {
    public:
         Component(string name) : m_strCompname(name){}
         virtual ~Component(){}
         virtual void Operation() = 0;
         virtual void Add(Component *) = 0;
         virtual void Remove(Component *) = 0;
         virtual Component *GetChild(int) = 0;
         virtual string GetName()
         {
              return m_strCompname;
         }
         virtual void Print() = 0;
    protected:
         string m_strCompname;
    };
    class Leaf : public Component
    {
    public:
         Leaf(string name) : Component(name)
         {}
         void Operation()
         {
              cout<<"I'm "<<m_strCompname<<endl;
         }
         void Add(Component *pComponent){}
         void Remove(Component *pComponent){}
         Component *GetChild(int index)
         {
              return NULL;
         }
         void Print(){}
    };
    class Composite : public Component
    {
    public:
         Composite(string name) : Component(name)
         {}
         ~Composite()
         {
              vector<Component *>::iterator it = m_vecComp.begin();
              while (it != m_vecComp.end())
              {
                   if (*it != NULL)
                   {
                        cout<<"----delete "<<(*it)->GetName()<<"----"<<endl;
                        delete *it;
                        *it = NULL;
                   }
                   m_vecComp.erase(it);
                   it = m_vecComp.begin();
              }
         }
         void Operation()
         {
              cout<<"I'm "<<m_strCompname<<endl;
         }
         void Add(Component *pComponent)
         {
              m_vecComp.push_back(pComponent);
         }
         void Remove(Component *pComponent)
         {
              for (vector<Component *>::iterator it = m_vecComp.begin(); it != m_vecComp.end(); ++it)
              {
                   if ((*it)->GetName() == pComponent->GetName())
                   {
                        if (*it != NULL)
                        {
                             delete *it;
                             *it = NULL;
                        }
                        m_vecComp.erase(it);
                        break;
                   }
              }
         }
         Component *GetChild(int index)
         {
              if (index > m_vecComp.size())
              {
                   return NULL;
              }
              return m_vecComp[index - 1];
         }
         void Print()
         {
              for (vector<Component *>::iterator it = m_vecComp.begin(); it != m_vecComp.end(); ++it)
              {
                   cout<<(*it)->GetName()<<endl;
              }
         }
    private:
         vector<Component *> m_vecComp;
    };
    int main(int argc, char *argv[])
    {
         Component *pNode = new Composite("Beijing Head Office");
         Component *pNodeHr = new Leaf("Beijing Human Resources Department");
         Component *pSubNodeSh = new Composite("Shanghai Branch");
         Component *pSubNodeCd = new Composite("Chengdu Branch");
         Component *pSubNodeBt = new Composite("Baotou Branch");
         pNode->Add(pNodeHr);
         pNode->Add(pSubNodeSh);
         pNode->Add(pSubNodeCd);
         pNode->Add(pSubNodeBt);
         pNode->Print();
         Component *pSubNodeShHr = new Leaf("Shanghai Human Resources Department");
         Component *pSubNodeShCg = new Leaf("Shanghai Purchasing Department");
         Component *pSubNodeShXs = new Leaf("Shanghai Sales department");
         Component *pSubNodeShZb = new Leaf("Shanghai Quality supervision Department");
         pSubNodeSh->Add(pSubNodeShHr);
         pSubNodeSh->Add(pSubNodeShCg);
         pSubNodeSh->Add(pSubNodeShXs);
         pSubNodeSh->Add(pSubNodeShZb);
         pNode->Print();
         // 公司不景气,需要关闭上海质量监督部门
         pSubNodeSh->Remove(pSubNodeShZb);
         if (pNode != NULL)
         {
              delete pNode;
              pNode = NULL;
         }
         return 0;
    }

    实现要点

    1. Composite的关键之一在于一个抽象类,它既可以代表Leaf,又可以代表Composite;所以在实际实现时,应该最大化Component接口,Component类应为Leaf和Composite类尽可能多定义一些公共操作。Component类通常为这些操作提供缺省的实现,而Leaf和Composite子类可以对它们进行重定义;
    2. Component是否应该实现一个Component列表,在上面的代码中,我是在Composite中维护的列表,由于在Leaf中,不可能存在子Composite,所以在Composite中维护了一个Component列表,这样就减少了内存的浪费;
    3. 内存的释放;由于存在树形结构,当父节点都被销毁时,所有的子节点也必须被销毁,所以,我是在析构函数中对维护的Component列表进行统一销毁,这样就可以免去客户端频繁销毁子节点的困扰;
    4. 由于在Component接口提供了最大化的接口定义,导致一些操作对于Leaf节点来说并不适用,比如:Leaf节点并不能进行Add和Remove操作,由于Composite模式屏蔽了部分与整体的区别,为了防止客户对Leaf进行非法的Add和Remove操作,所以,在实际开发过程中,进行Add和Remove操作时,需要进行对应的判断,判断当前节点是否为Composite。

    组合模式的优点

    将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    使用场景

    1. 你想表示对象的部分-整体层次结构;
    2. 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

    引用大话设计模式的片段:“当发现需求中是体现部分与整体层次结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑组合模式了。”

     参考:

    http://www.jellythink.com/archives/149#prettyPhoto

    http://blog.csdn.net/wuzhekai1985/article/details/6667564

  • 相关阅读:
    ytu 2030: 求实数绝对值(水题)
    [PHP] 链表数据结构(单链表)
    PHP将数据写入指定文件中
    PHP获取文件后缀名
    PHP数组序列化和反序列化
    PHP二维数组(或任意维数组)转换成一维数组的方法汇总
    PHP获取文件大小的方法详解
    PHP中嵌套函数被调用时出现报错的问题
    PHP递归排序
    PHP实现简单倒计时
  • 原文地址:https://www.cnblogs.com/diegodu/p/4448009.html
Copyright © 2011-2022 走看看