C++设计模式——组合模式

C++设计模式——组合模式

概念

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。掌握组合模式的重点是要理解清楚 “部分/整体” 还有 ”单个对象“ 与 “组合对象” 的含义。

组合模式可以让客户端像修改配置文件一样简单的完成本来需要流程控制语句来完成的功能。

场景描述

    现有一个公司需要实现一个广播的功能,用来通知给公司所有部门重要的信息。

一步一步到组合模式

    抛开设计模式不说,如果单纯的想实现上述场景,简单的实现一个总公司类,一个财务部门类,一个人力资源部门类。 通过组合的方式,将财务部门和人力资源部门组合到公司类中, 只要有要广播的消息,通过调用组合进来的部门各自的方法即可。类图如下。

第一步

    如此实现最明显的缺点就是如果需要广播,那么必须通过每一个部门类的实例去调用各自部门的广播接口。于是将功能改成如下实现。通过一个抽象类来抽象出广播接口,各个部门和公司都通过继承实现抽象接口。如此实现可以将所有部门放在一个容器内,遍历实现其广播接口即可。类图如下:

第二步

    上述实现如果公司不扩大,永远有这几个部门并且没有分公司的话,是没问题的。如果再要添加一个部门,就必须实现一个部门类,再修改总公司类。这明显违背了开闭原则。既然我们通过抽象类来实现了其方法,那就可以实现抽象的增加,删除,查询接口。如此一来,如果要有新的部门, 那么只需要通过调用添加接口来增加新部门, 广播的时候,只需要遍历容器中的广播接口即可。实现类图如下。

第三步

    上述实现如果再要添加一个子公司,那子公司如果直接继承抽象类,那子公司是没有增删查部门接口的。所以将此类接口抽象到抽象类中。如此一来,类图修改如下图:

第四步

    上图就是透明组合模式。可是各部门本来是没有添加,删除,查询部门的权限的。 所以就有了如下的安全组合模式:

第五步

透明组合模式和安全组合模式的区别

  • 透明组合模式是标准模式, 但是足够透明,对于客户端来说,都是一致的。但是缺点就是不够安全,因为部门和公司还是有区别的。如果给部门中添加公司,编译的时候是不会出问题的,但是运行的时候才会报错。

  • 安全组合模式则是针对透明组合模式的问题的。 将错误提前到编译期。但是缺点是不够透明。

代码实现

//Component.h
#ifndef COMPONENTS_H_
#define COMPONENTS_H_

#include <iostream>
#include <vector>

using namespace std;

class Components
{
public:
    Components(std::string strName):m_strName(strName){}

    virtual void Operation() = 0;
    virtual void AddSubCompany(Components* subCompany);
    virtual void DelSubCompany(Components* subCompany);
    virtual Components* GetCompanyByIndex(int iIndex);

protected:
    std::string m_strName;
};

class ConcreteCompany : public Components
{
public:
    ConcreteCompany(std::string strName):Components(strName){}
    ~ConcreteCompany();

    virtual void Operation();
    virtual void AddSubCompany(Components* subCompany);
    virtual void DelSubCompany(Components* subCompany);
    virtual Components* GetCompanyByIndex(int iIndex);
private:
    std::vector<Components*> m_vecSubItem;
};

class FinanceDepartment : public Components
{
public:
    FinanceDepartment(std::string strName):Components(strName){}

    virtual void Operation();
};

class HRDepartment : public Components
{
public:
    HRDepartment(std::string strName):Components(strName){}

    virtual void Operation();
};

#endif
//Component.cpp
#include "Company.h"
#include <algorithm>

#ifndef SAFE_DELETE
#define SAFE_DELETE(p){if((p) != NULL){delete (p); (p) = NULL;}}
#endif

void Components::AddSubCompany( Components* subCompany )
{
    cout << "Have no realized!" << endl;
}

void Components::DelSubCompany( Components* subCompany )
{
    cout << "Have no realized!" << endl;
}

Components* Components::GetCompanyByIndex( int iIndex )
{
    cout << "Have no realized!" << endl;
    return NULL;
}

//******************//
//**CentralCompany**//
//******************//

ConcreteCompany::~ConcreteCompany()
{
    std::for_each(m_vecSubItem.begin(),m_vecSubItem.end(),[&](Components* item)
    {
        SAFE_DELETE(item);
    });
}

void ConcreteCompany::Operation()
{
    std::for_each(m_vecSubItem.begin(),m_vecSubItem.end(),[&](Components* item)
    {
        item->Operation();
    });
}

void ConcreteCompany::AddSubCompany( Components* subCompany )
{
    if (subCompany != NULL)
    {
        m_vecSubItem.push_back(subCompany);
    }
}

void ConcreteCompany::DelSubCompany( Components* subCompany )
{
    for (auto it = m_vecSubItem.begin(); it != m_vecSubItem.end(); ++it)
    {
        if ((*it) == subCompany)
        {
            m_vecSubItem.erase(it);
            SAFE_DELETE(subCompany);
            break;
        }
    }
}

Components* ConcreteCompany::GetCompanyByIndex( int iIndex )
{
    if (iIndex < 0 || iIndex > m_vecSubItem.size())
    {
        return NULL;
    }
    return m_vecSubItem[iIndex];
}

void FinanceDepartment::Operation()
{
    cout << m_strName.c_str() << endl;
}

void HRDepartment::Operation()
{
    cout << m_strName.c_str() << endl;
}
//main.cpp

#include <iostream>
#include "Company.h"

using namespace std;

int main()
{
    Components* Central = new ConcreteCompany("Central Company");
    Central->AddSubCompany(new FinanceDepartment("Central Finance Department"));
    Central->AddSubCompany(new HRDepartment("Central HR Department"));

    Components* Xian = new ConcreteCompany("Xi'An Company");
    Xian->AddSubCompany(new FinanceDepartment("Xi'An Finance Department"));
    Xian->AddSubCompany(new HRDepartment("Xi'An HR Department"));

    Central->AddSubCompany(Xian);
    Central->Operation();

    cout << "<<<<<>>>>>" << endl;
    Central->DelSubCompany(Xian);
    Central->Operation();

    return 0;
}

输出

Central Finance Department

Central HR Department

Xi’An Finance Department

Xi’An HR Department

<<<<<>>>>>

Central Finance Department

Central HR Department

组合模式优缺点

优点:

  • 简化客户端调用,实现符合开闭原则。

缺点:

  • 如果业务逻辑负责,则实现组合模式比较困难。

适用场景

  • 适用于“整体-部分”层次的树形结构的。(文件目录实现)

  • 客户端对组合对象统一的使用所有对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值