设计模式(十二)——组合模式
一、组合模式简介
1、组合模式简介
组合模式将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
组合模式为解决组件之间的递归组合提供了解决的办法,主要分为两个派生类:
A、Leaf是叶子结点,是不含有子组件的结点
B、Composite是含有子组件的类
组合模式有透明组合模式和安全组合模式两种模式。
透明组合模式特点:
A、在Component中定义了用于访问和管理子构件的接口,好处是确保所有的构件类都有相同的接口。
B、在Client看来,Leaf与Composite所提供的接口一致,Client可以相同地对待所有的对象。
安全组合模式特点:
在Component中不定义任何用于访问和管理子构建的接口,而在 Composite中声明并实现。因为不需要向Leaf提供管理成员对象的接口,对于Leaf来说,Client不可能调用到管理子构件对象的接口。
透明组合模式和安全组合模式的区别:
A、透明组合模式的缺点是不够安全,因为Leaf和Composite在本质上是有区别的。Leaf不可能有下一个层级,因此为其提供add()、remove()、getChild() 等接口没有意义。在运行阶段如果调用这些接口可能会出错(如果没有提供相应的异常处理)。
B、安全组合模式的缺点是不够透明,因为Leaf和Composite具有不同的接口,且Composite中用于访问和管理子构件的接口没有在Component中定义,因此Client不能完全针对抽象编程,必须有区别地对待Leaf和 Composite。
组合模式的UML图:
在Component中声明所有用来管理子对象的方法,其中包括add、remove、getChild,实现Component接口的所有子类都具备了add、remove、getChild。好处就是叶节点和子节点对于外界没有区别,具备完全一致的行为接口。但因为Leaf类本身不具备add()、remove()、getChild()方法的功能,所以Leaf类继承的Component基类的add、remove、getChild是没有意义的。
2、组合模式角色
Component抽象构件:为组合中的对象声明接口,实现所有类共有接口的默认行为(add、remove、getChild)。声明一个接口用于访问和管理Component 的子部件。
Component::operatation:定义了各个组件共有的行为接口,由各个组件的具体实现
Component::add添加一个子组件
Component::remove::删除一个子组件.
Component::getChild:获得子组件的指针
Leaf叶子节点:在组合中是叶节点对象,叶节点没有子节点。
Composite枝构件:定义有子节点行为,用来存储子部件,重新实现在Component接口中与子部件有关的操作,比如add、remove、getChild、operation。
3、组合模式优缺点
优点:
A、组合模式清晰地定义了包含基本对象(Leaf)和组合对象(Composite)的类层次结构的复杂对象。基本对象可以被组合成更复杂的组合对象,而组合对象又可以被组合,客户代码可以忽略层次的差异,任何用到基本对象的地方都可以使用组合对象,方便对整个层次结构进行控制。
B、用户不用关心到底是处理一个叶子节点还是处理一个组合组件。
C、组合模式让客户可以一致的使用组合结构和单个对象。
D、在组合模式中,增加新的叶子构件和容器构件很方便,无须对现有类进行任何修改,符合“开闭原则”。
E、为树形结构提供了一种灵活的解决方案,通过递归组合容器对象和叶子对象,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点:
使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。
4、组合模式使用场景
组合模式使用场景:
A、当发现需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,应该考虑用组合模式。
B、基本对象可以被组合成更复杂的组合对象,而组合对象又可以被组合,客户代码中,任何用到基本对象的地方都可以使用组合对象。
二、 组合模式实现
Component抽象类:
#ifndef COMPONENT_H #define COMPONENT_H #include <iostream> using namespace std; //Component抽象基类,为组合中的对象声明接口,声明了类共有接口的缺省行为(add,remove,getChild函数), //声明一个接口函数可以访问Component的子组件 class Component { public: //纯虚函数,只提供接口,没有默认的实现 virtual void operation() = 0; // 虚函数,提供接口,默认的实现打印当前调用函数 virtual void add(Component* com) { cout << "Component::add" << endl; } virtual void remove(Component* com) { cout << "Component::remove" << endl; } virtual Component* getChild(int index) { cout << "Component::getChild" << endl; } protected: //只可以被子类调用,不对外开放 Component(){} }; #endif // COMPONENT_H
Leaf叶子节点类:
#ifndef LEAF_H #define LEAF_H #include "Component.h" //Leaf是叶子结点,是不含有子组件的结点类,继承基类add、remove、getChild方法 class Leaf : public Component { public: Leaf(){} //需要实现基类的operation接口 virtual void operation() { cout << "Leaf::operation" << endl; } }; #endif // LEAF_H
Composite类:
#ifndef COMPOSITE_H #define COMPOSITE_H #include "Component.h" #include <vector> //Composite:含有子组件的类 class Composite : public Component { public: //实现所有接口 void operation() { cout << "Composite::operation" << endl; vector<Component*>::iterator iter = this->m_comVec.begin(); for(iter;iter!= this->m_comVec.end();iter++) { (*iter)->operation(); } } void add(Component* com) { cout << "Composite::add an component" << endl; m_comVec.push_back(com); } void remove(Component* com) { cout << "Composite::remove an component" << endl; vector<Component*>::iterator iter = this->m_comVec.begin(); for(iter;iter!= this->m_comVec.end();iter++) { if(*iter == com) m_comVec.erase(iter); } } Component* getChild(int index) { if(index < 0 || index > this->m_comVec.size()) { return NULL; } return this->m_comVec[index]; } private: //vector来保存子组件 vector<Component*> m_comVec; }; #endif // COMPOSITE_H
客户调用程序:
#include "Composite.h" #include "Leaf.h" int main() { //不管是叶子Leaf还是Composite对象pRoot、pCom都实现了operation接口,可以一致对待, //直接调用operation(),体现了“使得用户对单个对象和组合对象的使用具有一致性。” Composite* pRoot = new Composite(); //组合对象添加叶子节点 pRoot->add(new Leaf()); Leaf* pLeaf1 = new Leaf(); Leaf* pLeaf2 = new Leaf(); //叶子节点只实现了operation方法,其他add、remove、getChild都继承自基类 pLeaf1->add(pLeaf2);//执行基类add pLeaf1->remove(pLeaf2);//执行基类remove //执行叶子Operation操作 pLeaf1->operation(); //组合对象实现了基类Component的所有接口 Composite* pCom = new Composite(); //组合对象添加叶子节点 pCom->add(pLeaf1); //组合对象添加叶子节点 pCom->add(pLeaf2); //执行组合对象Operation操作 pCom->operation(); //组合对象添加组合对象 pRoot->add(pCom); //执行组合对象Operation操作 pRoot->operation(); return 0; }
三、组合模式实例
一个集团公司,有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是子部门。整个公司的部门拓扑图就是一个树形结构。
Company公司抽象类:
#ifndef COMPANY_H #define COMPANY_H #include <string> #include <iostream> #include <algorithm> using namespace std; //公司抽象类 class Company { public: Company(string name) { m_name = name; } //增加节点 virtual void add(Company* com) = 0; //显示 virtual void show(int depth) = 0; //删除节点 virtual void remove(Company* com) = 0; //职责 virtual void duty() = 0; bool operator==(const Company& com) { return m_name == com.m_name; } protected: string m_name; }; #endif // COMPANY_H
ConcreteCompany具体公司类:
#ifndef CONCRETECOMPANY_H #define CONCRETECOMPANY_H #include "Company.h" #include <list> //具体公司类 class ConcreteCompany : public Company { public: ConcreteCompany(string name):Company(name) { m_childs = new list<Company*>; } ~ConcreteCompany() { for(list<Company*>::iterator it = m_childs->begin(); it != m_childs->end(); it++) delete *it; delete m_childs; } //增加子节点 void add(Company* com) { m_childs->push_back(com); } //删除子节点 void remove(Company* com) { for(list<Company*>::iterator it = m_childs->begin(); it != m_childs->end(); it++) { if(**it == *com) { m_childs->erase(it); break; } } } //显示本节点的子节点 void show(int depth) { for(int i = 0;i < depth; i++) std::cout<<"-"; //显示公司节点名称 std::cout << m_name << std::endl; for(list<Company*>::iterator it = m_childs->begin(); it != m_childs->end(); it++) (*it)->show(depth + 4);//显示本节点的子节点名称 } //显示子节点的职责 void duty() { for(list<Company*>::iterator it = m_childs->begin(); it != m_childs->end(); it++) (*it)->duty();//显示本节点的子节点职责 } private: list<Company*>* m_childs; }; #endif // CONCRETECOMPANY_H
FinanceDepartment财务部门类:
#ifndef FINANCEDEPARTMENT_H #define FINANCEDEPARTMENT_H #include "Company.h" using namespace std; //公司财务部门 class FinanceDepartment: public Company { public: FinanceDepartment(string name):Company(name){} void add(Company* com) { } void remove(Company* com) { } //显示财务部门名称 void show(int depth) { for(int i = 0;i < depth; i++) std::cout<<"-"; std::cout << m_name << std::endl; } //显示财务部分职责 void duty() { std::cout<< m_name <<" Financial Management"<<std::endl; } }; #endif // FINANCEDEPARTMENT_H
HRDepartment人力资源部门类:
#ifndef HRDEPARTMENT_H #define HRDEPARTMENT_H #include "Company.h" class HRDepartment : public Company { public: HRDepartment(string name):Company(name){} //显示人力资源部分名称 void show(int depth) { for(int i=0;i < depth;i++) std::cout<<"-"; std::cout<< m_name <<std::endl; } void add(Company* com) { } void remove(Company* com) { } //显示人力资源部分职责 void duty() { std::cout<< m_name <<" staff recruitment and employment"<<std::endl; } }; #endif // HRDEPARTMENT_H
客户调用程序:
#include "Company.h" #include "ConcreteCompany.h" #include "FinanceDepartment.h" #include "HRDepartment.h" int main() { //北京总公司 Company* root = new ConcreteCompany("BeiJing Head Office"); root->add(new HRDepartment("HRDepartment of Head Office")); root->add(new FinanceDepartment("FinanceDepartment of Head Office")); //华东上海分公司 Company* ShangHaicomp=new ConcreteCompany("China East ShangHai Branch Office"); ShangHaicomp->add(new HRDepartment("HRDepartment of ShangHai Branch Office")); ShangHaicomp->add(new FinanceDepartment("FinanceDepartment of ShangHai Branch Office")); //华东分公司隶属北京总公司 root->add(ShangHaicomp); //华东分公司南京办事处 Company* NanJingOffice=new ConcreteCompany("NanJing Office"); NanJingOffice->add(new HRDepartment("HRDepartment of NanJing Office")); NanJingOffice->add(new FinanceDepartment("FinanceDepartment of NanJing Office")); //南京办事处隶属华东分公司 ShangHaicomp->add(NanJingOffice); //西南成都分公司 Company* ChengDucomp=new ConcreteCompany("China SouthWest ChengDu Branch Office"); ChengDucomp->add(new HRDepartment("HRDepartment of ChengDu Branch Office")); ChengDucomp->add(new FinanceDepartment("FinanceDepartment of ChengDu Branch Office")); //西南分公司隶属北京总公司 root->add(ChengDucomp); Company* KunMingOffice=new ConcreteCompany("KunMing Office"); KunMingOffice->add(new HRDepartment("HRDepartment of KunMing Office")); KunMingOffice->add(new FinanceDepartment("FinanceDepartment of KunMing Office")); //昆明办事处隶属西南分公司 ChengDucomp->add(KunMingOffice); //显示公司的树形结构 std::cout<<"Structure:"<<std::endl; root->show(1); //显示公司所有部门的职责 std::cout<<std::endl<<"Duty:"<<std::endl; root->duty(); delete KunMingOffice,ChengDucomp; delete NanJingOffice,ShangHaicomp; delete root; return 0; }