對於樹形結構,當容器對象(如文件夾)的某一個方法被調用時,將遍歷整個樹形結構,尋找也包含這個方法的成員對象(能夠是容器對象,也能夠是葉子對象,如子文件夾和文件)並調用執行。ios
因爲容器對象和葉子對象在功能上的區別,在使用這些對象的客戶端代碼中必須有區別地對待容器對象和葉子對象,而實際上大多數狀況下客戶端但願一致地處理它們,由於對於這些對象的區別對待將會使得程序很是複雜。函數
組合模式描述瞭如何將容器對象和葉子對象進行遞歸組合,使得用戶在使用時無須對它們進行區分,能夠一致地對待容器對象和葉子對象,這就是組合模式的模式動機。spa
組合(Composite)模式將對象組合成樹形結構以表示「部分-總體」的層次結構,使得用戶對單個對象和組合對象的使用具備一致性。設計
組合模式(Composite)將小對象組合成樹形結構,使用戶操做組合對象如同操做一個單個對象。組合模式定義了「部分-總體」的層次結構,基本對象能夠被組合成更大的對象,並且這種操做是可重複的,不斷重複下去就能夠獲得一個很是大的組合對象,但這些組合對象與基本對象擁有相同的接口,於是組合是透明的,用法徹底一致。code
組合模式就是把一些現有的對象或者元素,通過組合後組成新的對象,新的對象提供內部方法,可讓咱們很方便的完成這些元素或者內部對象的訪問和操做。咱們也能夠把組合對象理解成一個容器,容器提供各類訪問其內部對象或者元素的API,咱們只須要使用這些方法就能夠操做它了。對象
Component:blog
爲組合中的對象聲明接口;在適當的狀況下,實現全部類共有接口的缺省行爲;聲明一個接口用於訪問和管理Component的子組件。遞歸
Leaf:接口
在組合中表示葉節點對象,葉節點沒有子節點;在組合中定義葉節點的行爲。ip
Composite:
定義有子部件的那些部件的行爲;存儲子部件。
Client:
經過Component接口操做組合部件的對象。
#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) { cout << "----delete " << (*it)->GetName() << "----" << endl; 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; }; //靜態類型是Componet,可是其動態類型能夠是leaf,也能夠是Composite //從而造成樹狀關係 int main() { 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(); cout << endl; //成都分公司效益很差,須要將其關閉; pNode->Remove(pSubNodeCd); 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(); cout << endl; // 上海部門不景氣,須要關閉上海質量監督部門; pSubNodeSh->Remove(pSubNodeShZb); if (pNode != NULL) { delete pNode; pNode = NULL; } system("pause"); return 0; }
Composite的關鍵之一在於一個抽象類,它既能夠表明Leaf,又能夠表明Composite;因此在實際實現時,應該最大化Component接口,Component類應爲Leaf和Composite類儘量多定義一些公共操做。Component類一般爲這些操做提供缺省的實現,而Leaf和Composite子類能夠對它們進行重定義;
Component是否應該實現一個Component列表,在上面的代碼中,我是在Composite中維護的列表,因爲在Leaf中,不可能存在子Composite,因此在Composite中維護了一個Component列表,這樣就減小了內存的浪費;
內存的釋放;因爲存在樹形結構,當父節點都被銷燬時,全部的子節點也必須被銷燬,因此,在析構函數中對維護的Component列表進行統一銷燬,這樣就能夠免去客戶端頻繁銷燬子節點的困擾;
因爲在Component接口提供了最大化的接口定義,致使一些操做對於Leaf節點來講並不適用,好比:Leaf節點並不能進行Add和Remove操做,因爲Composite模式屏蔽了部分與總體的區別,爲了防止客戶對Leaf進行非法的Add和Remove操做,因此,在實際開發過程當中,進行Add和Remove操做時,須要進行對應的判斷,判斷當前節點是否爲Composite。
能夠清楚地定義分層次的複雜對象,表示對象的所有或部分層次,使得增長新構件也更容易。
客戶端調用簡單,客戶端能夠一致的使用組合結構或其中單個對象。
定義了包含葉子對象和容器對象的類層次結構,葉子對象能夠被組合成更復雜的容器對象,而這個容器對象又能夠被組合,這樣不斷遞歸下去,能夠造成複雜的樹形結構。
更容易在組合體內加入對象構件,客戶端沒必要由於加入了新的對象構件而更改原有代碼。
使設計變得更加抽象,對象的業務規則若是很複雜,則實現組合模式具備很大挑戰性,並且不是全部的方法都與葉子對象子類都有關聯。
增長新構件時可能會產生一些問題,很難對容器中的構件類型進行限制。
你想表示對象的部分-總體層次結構;
但願用戶忽略組合對象與單個對象的不一樣,用戶將統一地使用組合結構中的全部對象。
當發現需求中是體現部分與總體層次結構時,以及你但願用戶能夠忽略組合對象與單個對象的不一樣,統一地使用組合結構中的全部對象時,就應該考慮組合模式了。