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
组合模式优缺点
优点:
- 简化客户端调用,实现符合开闭原则。
缺点:
- 如果业务逻辑负责,则实现组合模式比较困难。
适用场景
适用于“整体-部分”层次的树形结构的。(文件目录实现)
客户端对组合对象统一的使用所有对象。