对于树形结构,当容器对象(如文件夹)的某一个方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员对象(可以是容器对象,也可以是叶子对象,如子文件夹和文件)并调用执行。(递归调用)
由于容器对象和叶子对象在功能上的区别,在使用这些对象的客户端代码中必须有区别地对待容器对象和叶子对象,而实际上大多数情况下客户端希望一致地处理它们,因为对于这些对象的区别对待将会使得程序非常复杂。
组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象,这就是组合模式的模式动机。
模式定义
组合模式(Composite Pattern):组合多个对象形成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
组合模式又可以称为“整体-部分”(Part-Whole)模式,属于对象的结构模式,它将对象组织到树结构中,可以用来描述整体与部分的关系。
组合模式的分类
1) 将管理子元素的方法定义在Composite类中。
2) 将管理子元素的方法定义在Component接口中,这样Leaf类就需要对这些方法空实现。
适用性
1).你想表示对象的部分-整体层次结构
2).你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
模式优点:
1) ? 定义了包含基本对象和组合对象的类层次结构 基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到 基本对象的地方都可以使用组合对象。
2) ? 简化客户代码 客户可以一致地使用组合结构和单个对象。通常用户不知道 (也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码 , 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
3) ? 使得更容易增加新类型的组件 新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component类而改变。
4) ? 使你的设计变得更加一般化 容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
ULM图
组合模式包含如下角色:
? Component: 抽象构件
? Leaf: 叶子构件
? Composite: 容器构件
? Client: 客户类
实例
公司:
一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。
下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。
实现代码如下:
#include <iostream>
#include <list>
#include <memory>
#include <utility>
#include <cstddef>using namespace std;class Company
{
public:Company(string name) { m_name = name; }virtual ~Company() {}virtual void Add(std::unique_ptr<Company>) {}//这里默认实现为空函数,leaf节点中不用实现为空了virtual void Show(int depth) {} // 这里默认实现为空函数,leaf节点中不用实现为空了
protected:string m_name;
};//具体公司
class ConcreteCompany : public Company
{
public:ConcreteCompany(string name) : Company(name) {}virtual ~ConcreteCompany() {for (auto& company : m_listCompany){company.reset(nullptr);}}void Add(std::unique_ptr<Company> pCom) { m_listCompany.push_back(std::move(pCom)); } //位于树的中间,可以增加子树void Show(int depth){for (int i = 0; i < depth; i++)cout << "-";cout << m_name << endl;auto iter = m_listCompany.begin();for (; iter != m_listCompany.end(); iter++) //显示下层结点(*iter)->Show(depth + 2);}
private:list<std::unique_ptr<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()
{auto root = std::make_unique<ConcreteCompany>("总公司");auto leaf1 = std::make_unique < FinanceDepartment>("总公司财务部");auto leaf2 = std::make_unique < HRDepartment>("总公司人力资源部");root->Add(std::move(leaf1));root->Add(std::move(leaf2));//分公司auto mid1 = std::make_unique < ConcreteCompany>("杭州分公司");auto leaf3 = std::make_unique < FinanceDepartment>("杭州分公司财务部");auto leaf4 = std::make_unique < HRDepartment>("杭州分公司人力资源部");mid1->Add(std::move(leaf3));mid1->Add(std::move(leaf4));root->Add(std::move(mid1));//分公司auto mid2 = std::make_unique < ConcreteCompany>("上海分公司");auto leaf5 = std::make_unique < FinanceDepartment>("上海分公司财务部");auto leaf6 = std::make_unique < HRDepartment>("上海分公司人力资源部");mid2->Add(std::move(leaf5));mid2->Add(std::move(leaf6));root->Add(std::move(mid2));root->Show(0);return 0;
}
运行结果如下: