结构型模式のComposite模式
Composite组合模式
作用:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
UML图如下:
在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等,这样实现Component接口的所有子类都具备了Add和Remove。
这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备 完全一致的行为 接口。
但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的 功能,所以实现它是没有意义的。
何时使用组合模式:
当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。
基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
用户不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合二写一些选择判断语句了。
组合模式让客户可以一致地使用组合结构和单个对象。
抽象基类:
1)Component:为组合中的对象声明接口,声明了类共有接口的缺省行为(如这里的Add,Remove,GetChild函数),声明一个接口函数可以访问Component的子组件.
接口函数:
1)Component::Operatation:定义了各个组件共有的行为接口,由各个组件的具体实现.
2)Component::Add添加一个子组件
3)Component::Remove::删除一个子组件.
4)Component::GetChild:获得子组件的指针.
说明:
Component模式是为解决组件之间的递归组合提供了解决的办法,它主要分为两个派生类:
1)、Leaf是叶子结点,也就是不含有子组件的结点
2)、Composite是含有子组件的类.
举一个例子来说明这个模式,在UI的设计中,最基本的控件是诸如Button、Edit这样的控件,相当于是这里的Leaf组件,而比较复杂的控件 比如Panel则可也看做是由这些基本的组件组合起来的控件,相当于这里的Composite,它们之间有一些行为含义是相同的,比如在控件上作一个点 击,移动操作等等的,这些都可以定义为抽象基类中的接口虚函数,由各个派生类去实现之,这些都会有的行为就是这里的Operation函数,而添加、删除 等进行组件组合的操作只有非叶子结点才可能有,所以虚拟基类中只是提供接口而且默认的实现是什么都不做。
代码如下:
1 //Component.h 2 3 #pragma once 4 class Component 5 { 6 public: 7 8 virtual ~Component(void); 9 10 //纯虚函数,只提供接口,没有默认的实现 11 virtual void Operator() = 0; 12 13 // 虚函数,提供接口,有默认的实现就是什么都不做 14 virtual void Add(Component*); 15 virtual void Remove(Component*); 16 virtual Component* GetChild(int index); 17 protected: 18 Component(void); 19 }; 20 21 //Component.cpp 22 23 #include "StdAfx.h" 24 #include "Component.h" 25 #include <iostream> 26 using std::endl; 27 using std::cout; 28 29 Component::Component(void) 30 { 31 } 32 33 34 Component::~Component(void) 35 { 36 } 37 38 void Component::Add(Component* com){ 39 40 } 41 42 void Component::Remove(Component* com){ 43 44 } 45 46 void Component::Operator(){ 47 48 } 49 50 Component* Component::GetChild(int index){ 51 return NULL; 52 }
1 //leaf.h 2 3 #pragma once 4 #include "component.h" 5 class Leaf : 6 public Component 7 { 8 public: 9 Leaf(void); 10 virtual ~Leaf(void); 11 virtual void Operator(); 12 }; 13 14 //leaf.cpp 15 16 #include "StdAfx.h" 17 #include "Leaf.h" 18 #include <iostream> 19 using std::cout; 20 using std::endl; 21 22 Leaf::Leaf(void) 23 { 24 } 25 26 27 Leaf::~Leaf(void) 28 { 29 } 30 31 void Leaf::Operator(){ 32 cout<<"Leaf::operator"<<endl; 33 }
1 //Composite.h 2 3 #pragma once 4 #include "component.h" 5 #include <vector> 6 using std::vector; 7 class Composite : 8 public Component 9 { 10 public: 11 Composite(void); 12 virtual ~Composite(void); 13 void Operator(); 14 void Add(Component*); 15 void Remove(Component*); 16 Component* GetChild(int index); 17 private: 18 vector<Component*> m_ComVec; 19 }; 20 21 //Composite.cpp 22 23 #include "StdAfx.h" 24 #include "Composite.h" 25 #include <iostream> 26 #include <algorithm> 27 using std::endl; 28 using std::cout; 29 30 Composite::Composite(void) 31 { 32 } 33 34 35 Composite::~Composite(void) 36 { 37 } 38 39 void Composite::Add(Component* com){ 40 this->m_ComVec.push_back(com); 41 } 42 43 void Composite::Remove(Component* com){ 44 45 this->m_ComVec.erase(find(this->m_ComVec.begin(),this->m_ComVec.end(),com)); 46 } 47 48 void Composite::Operator(){ 49 cout<<"Composite::Operator"<<endl; 50 vector<Component*>::iterator iter = this->m_ComVec.begin(); 51 for(;iter != this->m_ComVec.end();iter++){ 52 (*iter)->Operator(); 53 } 54 } 55 56 Component* Composite::GetChild(int index){ 57 if(index<0 || index >(int)(this->m_ComVec.size())) 58 return NULL; 59 return this->m_ComVec[index]; 60 }
1 // main.cpp : 定义控制台应用程序的入口点。 2 3 #include "stdafx.h" 4 #include "Leaf.h" 5 #include "Composite.h" 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 Composite* pRoot = new Composite(); 10 pRoot->Add(new Leaf()); 11 12 Leaf* pLeaf1 = new Leaf(); 13 Leaf* pLeaf2 = new Leaf(); 14 15 pRoot->Add(pLeaf1); 16 pRoot->Add(pLeaf2); 17 pRoot->Remove(pLeaf1); 18 19 pRoot->Operator(); 20 21 return 0; 22 }
组合的另一个例子:摘自~~~
DP书上给出的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。注意两个字“树形”。这 种树形结构在现实生活中随处可见,比如一个集团公司,它有一个母公司,下设很多家子公司。不管是母公司还是子公司,都有各自直属的财务部、人力资源部、销 售部等。对于母公司来说,不论是子公司,还是直属的财务部、人力资源部,都是它的部门。整个公司的部门拓扑图就是一个树形结构。
下面给出组合模式的UML图。从图中可以看到,FinanceDepartment、HRDepartment两个类作为叶结点,因此没有定义添加函数。
而ConcreteCompany类可以作为中间结点,所以可以有添加函数。那么怎么添加呢?这个类中定义了一个链表,用来放添加的元素。
1 //Company.h 2 3 #pragma once 4 #include <string> 5 using std::string; 6 class Company 7 { 8 public: 9 Company(string name); 10 virtual ~Company(void); 11 virtual void Add(Company* pCom); 12 virtual void Show(int depth); 13 protected: 14 string m_name; 15 }; 16 17 //Company.cpp 18 19 #include "StdAfx.h" 20 #include "Company1.h" 21 22 Company1::Company1(string name) 23 { 24 m_name=name; 25 } 26 27 Company1::~Company1(void) 28 { 29 } 30 31 void Company1::Add(Company1* pCom){ 32 } 33 34 void Company1::Show(int depth){ 35 }
1 //ConcreteCompany.h 2 3 #pragma once 4 #include "company.h" 5 #include <list> 6 using std::list; 7 class ConcreteCompany : 8 public Company 9 { 10 public: 11 ConcreteCompany(string name); 12 virtual ~ConcreteCompany(void); 13 void Add(Company* pCom); 14 void Show(int depth); 15 private: 16 list<Company *> m_listCompany; 17 }; 18 19 //ConcreteCompany.cpp 20 21 #include "StdAfx.h" 22 #include "ConcreteCompany.h" 23 #include <iostream> 24 using std::endl; 25 using std::cout; 26 27 ConcreteCompany::ConcreteCompany(string name):Company(name) 28 { 29 } 30 31 32 ConcreteCompany::~ConcreteCompany(void) 33 { 34 } 35 36 void ConcreteCompany::Add(Company* pCom){ 37 m_listCompany.push_back(pCom); 38 } 39 40 void ConcreteCompany::Show(int depth){ 41 for(int i = 0;i<depth;i++) 42 cout<<"-"; 43 cout<<this->m_name<<endl; 44 list<Company*>::iterator iter=m_listCompany.begin(); 45 for(;iter!=m_listCompany.end();iter++) 46 (*iter)->Show(depth+2); 47 }
1 //FinanceDepartment.h 2 3 #pragma once 4 #include "company.h" 5 class FinanceDepartment : 6 public Company 7 { 8 public: 9 FinanceDepartment(string name); 10 virtual ~FinanceDepartment(void); 11 virtual void Show(int depth); 12 }; 13 14 15 //FinanceDepartment.cpp 16 17 #include "StdAfx.h" 18 #include "FinanceDepartment.h" 19 #include <iostream> 20 using std::endl; 21 using std::cout; 22 23 FinanceDepartment::FinanceDepartment(string name):Company1(name) 24 { 25 } 26 27 28 FinanceDepartment::~FinanceDepartment(void) 29 { 30 } 31 32 void FinanceDepartment::Show(int depth){ 33 for(int i=0;i<depth;i++) 34 cout<<"-"; 35 cout<<this->m_name<<endl; 36 }
1 //HRDepartment.h 2 3 #pragma once 4 #include "company.h" 5 class HRDepartment : 6 public Company 7 { 8 public: 9 HRDepartment(string name); 10 virtual ~HRDepartment(void); 11 virtual void Show(int depth); 12 }; 13 14 //HRDepartment.cpp 15 16 #include "StdAfx.h" 17 #include "HRDepartment.h" 18 #include <iostream> 19 using std::endl; 20 using std::cout; 21 22 HRDepartment::HRDepartment(string name):Company(name) 23 { 24 } 25 26 HRDepartment::~HRDepartment(void) 27 { 28 } 29 30 void HRDepartment::Show(int depth){ 31 for(int i=0;i<depth;i++) 32 cout<<"-"; 33 cout<<this->m_name<<endl; 34 }
1 // Company.cpp : 定义控制台应用程序的入口点。 2 3 #include "stdafx.h" 4 #include "ConcreteCompany.h" 5 #include "FinanceDepartment.h" 6 #include "HRDepartment.h" 7 8 int _tmain(int argc, _TCHAR* argv[]) 9 { 10 Company *root = new ConcreteCompany("总公司"); 11 Company *leaf1 = new FinanceDepartment("财务部"); 12 Company *leaf2 = new HRDepartment("人力资源部"); 13 root->Add(leaf1); 14 root->Add(leaf2); 15 16 Company1* mid1 = new ConcreteCompany("分公司A"); 17 Company1* leaf3 = new FinanceDepartment("财务部"); 18 Company1* leaf4 = new HRDepartment("人力资源部"); 19 mid1->Add(leaf3); 20 mid1->Add(leaf4); 21 root->Add(mid1); 22 23 Company1 *mid2=new ConcreteCompany("分公司B"); 24 FinanceDepartment *leaf5=new FinanceDepartment("财务部"); 25 HRDepartment *leaf6=new HRDepartment("人力资源部"); 26 mid2->Add(leaf5); 27 mid2->Add(leaf6); 28 root->Add(mid2); 29 root->Show(0); 30 31 delete leaf1; delete leaf2; 32 delete leaf3; delete leaf4; 33 delete leaf5; delete leaf6; 34 delete mid1; delete mid2; 35 delete root; 36 return 0; 37 }
测试环境:vs 2010
上面的实现方式有缺点,就是内存的释放不好,需要客户自己动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所 有的指针都是存在ConcreteCompany类的链表中。C++的麻烦,没有垃圾回收机制。上面的实现方式有缺点,就是内存的释放不好,需要客户自己 动手,非常不方便。有待改进,比较好的做法是让ConcreteCompany类来释放。因为所有的指针都是存在ConcreteCompany类的链表 中。C++的麻烦,没有垃圾回收机制。
本文经过博主修改、原文参考:~~~