博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[设计模式] 8 组合模式 Composite
阅读量:6977 次
发布时间:2019-06-27

本文共 6201 字,大约阅读时间需要 20 分钟。

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<
<
::iterator iter=m_listCompany.begin(); for(; iter != m_listCompany.end(); iter++) //显示下层结点 (*iter)->Show(depth + 2); }private: list
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<
<
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 
#include
#include
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 "<
<
::iterator it = m_vecComp.begin(); while (it != m_vecComp.end()) { if (*it != NULL) { cout<<"----delete "<<(*it)->GetName()<<"----"<
::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
::iterator it = m_vecComp.begin(); it != m_vecComp.end(); ++it) { cout<<(*it)->GetName()<
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

你可能感兴趣的文章
在macos上基于python2.7安装PyQt5
查看>>
69亿美元英伟达史上最大收购!这家基金又赢了
查看>>
阿里云双12服务器和阿里云双12数据库活动又开始了
查看>>
百度成立小度蓝牙联盟,DMA+小度App打造蓝牙语音风口
查看>>
第二十章:异步和文件I/O.(十三)
查看>>
第四范式完成C轮融资,金额超10亿元
查看>>
Java图形化:布局方式
查看>>
python 帮助文档、自我解释
查看>>
helm安装配置
查看>>
离线安装k8s 1.9.0
查看>>
my项目的总结2015.8.26编
查看>>
Linux 基金会宣布红队项目,致力于孵化开源安全工具
查看>>
索尼发布无人机相机专利,支持眼部对焦
查看>>
js字符串方法
查看>>
sql server几种读写分离方案的比较
查看>>
Ubuntu阿里云搭建Mono.net环境
查看>>
一对一直播app源码功能操详解方案分享
查看>>
liunx软件安装
查看>>
CentOS7系统下修改网卡为eth0
查看>>
vsftpd企业应用快速部署文档
查看>>