從定義中能夠得到使用組合模式的環境為:在設計中想表示物件的“部分-總體”層次結構;希望使用者忽略組合物件與單個物件的不同,統一地使用組合結構中的全部物件。
看下組合模式的組成。
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。