从定义中能够得到使用组合模式的环境为:在设计中想表示对象的“部分-总体”层次结构;希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的全部对象。
看下组合模式的组成。
1) 抽象构件角色Component:它为组合中的对象声明接口。也能够为共同拥有接口实现缺省行为。
2) 树叶构件角色Leaf:在组合中表示叶节点对象——没有子节点。实现抽象构件角色声明的接口。
3) 树枝构件角色Composite:在组合中表示分支节点对象——有子节点,实现抽象构件角色声明的接口;存储子部件。
Component:
为组合中的对象声明接口;
在适当的情况下,实现全部类共同拥有接口的缺省行为;
声明一个接口用于訪问和管理Component的子组件。
Leaf:
在组合中表示叶节点对象,叶节点没有子节点;
在组合中定义叶节点的行为。
Composite:
定义有子部件的那些部件的行为;
存储子部件。
Client:
通过Component接口操作组合部件的对象。
组合模式中必须提供对子对象的管理方法。不然无法完毕对子对象的加入删除等等操作,也就失去了灵活性和扩展性。
可是管理方法是在Component中就声明还是在Composite中声明呢?
一种方式是在Component里面声明全部的用来管理子类对象的方法,以达到Component接口的最大化(例如以下图所看到的)。目的就是为了使客户看来在接口层次上树叶和分支没有差别——透明性。但树叶是不存在子类的。因此Component声明的一些方法对于树叶来说是不适用的。这样也就带来了一些安全性问题。
还有一种方式就是仅仅在Composite里面声明全部的用来管理子类对象的方法(例如以下图所看到的)。这样就避免了上一种方式的安全性问题。可是因为叶子和分支有不同的接口,所以又失去了透明性。
《设计模式》一书觉得:在这一模式中。相对于安全性,我们比較强调透明性。
对于第一种方式中叶子节点内不须要的方法能够使用空处理或者异常报告的方式来解决。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 抽象的部件类描写叙述将来全部部件共同拥有的行为
class Component
{
public:Component(string name) : m_strCompname(name){}virtual ~Component(){}virtual void Operation() = 0;virtual void Add(Component *) = 0;virtual void Remove(Component *) = 0;virtual Component *GetChild(int) = 0;virtual string GetName(){return m_strCompname;}virtual void Print() = 0;
protected:string m_strCompname;
};
class Leaf : public Component
{
public:Leaf(string name) : Component(name){}void Operation(){cout<<"I'm "<<m_strCompname<<endl;}void Add(Component *pComponent){}void Remove(Component *pComponent){}Component *GetChild(int index){return NULL;}void Print(){}
};
class Composite : public Component
{
public:Composite(string name) : Component(name){}~Composite(){vector<Component *>::iterator it = m_vecComp.begin();while (it != m_vecComp.end()){if (*it != NULL){cout<<"----delete "<<(*it)->GetName()<<"----"<<endl;delete *it;*it = NULL;}m_vecComp.erase(it);it = m_vecComp.begin();}}void Operation(){cout<<"I'm "<<m_strCompname<<endl;}void Add(Component *pComponent){m_vecComp.push_back(pComponent);}void Remove(Component *pComponent){for (vector<Component *>::iterator it = m_vecComp.begin(); it != m_vecComp.end(); ++it){if ((*it)->GetName() == pComponent->GetName()){if (*it != NULL){delete *it;*it = NULL;}m_vecComp.erase(it);break;}}}Component *GetChild(int index){if (index > m_vecComp.size()){return NULL;}return m_vecComp[index - 1];}void Print(){for (vector<Component *>::iterator it = m_vecComp.begin(); it != m_vecComp.end(); ++it){cout<<(*it)->GetName()<<endl;}}
private:vector<Component *> m_vecComp;
};
int main(int argc, char *argv[])
{Component *pNode = new Composite("Beijing Head Office");Component *pNodeHr = new Leaf("Beijing Human Resources Department");Component *pSubNodeSh = new Composite("Shanghai Branch");Component *pSubNodeCd = new Composite("Chengdu Branch");Component *pSubNodeBt = new Composite("Baotou Branch");pNode->Add(pNodeHr);pNode->Add(pSubNodeSh);pNode->Add(pSubNodeCd);pNode->Add(pSubNodeBt);pNode->Print();Component *pSubNodeShHr = new Leaf("Shanghai Human Resources Department");Component *pSubNodeShCg = new Leaf("Shanghai Purchasing Department");Component *pSubNodeShXs = new Leaf("Shanghai Sales department");Component *pSubNodeShZb = new Leaf("Shanghai Quality supervision Department");pSubNodeSh->Add(pSubNodeShHr);pSubNodeSh->Add(pSubNodeShCg);pSubNodeSh->Add(pSubNodeShXs);pSubNodeSh->Add(pSubNodeShZb);pNode->Print();// 公司不景气。须要关闭上海质量监督部门pSubNodeSh->Remove(pSubNodeShZb);if (pNode != NULL){delete pNode;pNode = NULL;}return 0;
}
Composite的关键之中的一个在于一个抽象类。它既能够代表Leaf。又能够代表Composite;所以在实际实现时,应该最大化Component接口,Component类应为Leaf和Composite类尽可能多定义一些公共操作。
Component类通常为这些操作提供缺省的实现,而Leaf和Composite子类能够对它们进行重定义;
Component是否应该实现一个Component列表,在上面的代码中。我是在Composite中维护的列表。因为在Leaf中,不可能存在子Composite,所以在Composite中维护了一个Component列表,这样就降低了内存的浪费。
内存的释放;因为存在树形结构,当父节点都被销毁时,全部的子节点也必须被销毁,所以,我是在析构函数中对维护的Component列表进行统一销毁,这样就能够免去client频繁销毁子节点的困扰。
因为在Component接口提供了最大化的接口定义。导致一些操作对于Leaf节点来说并不适用,比方:Leaf节点并不能进行Add和Remove操作。因为Composite模式屏蔽了部分与总体的差别。为了防止客户对Leaf进行非法的Add和Remove操作,所以。在实际开发过程中,进行Add和Remove操作时,须要进行相应的推断,推断当前节点是否为Composite。