C++設計模式-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函數,而添加、刪除等進行組件組合的操作只有非葉子結點才可能有,所以虛擬基類中只是提供接口而且默認的實現是什么都不做。

代碼如下:

Composite.h

 1 #ifndef _COMPOSITE_H_
 2 #define _COMPOSITE_H_
 3 
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 /*
 9 Component抽象基類,為組合中的對象聲明接口,聲明了類共有接口的缺省行為(如這里的Add,Remove,GetChild函數),
10 聲明一個接口函數可以訪問Component的子組件.
11 */
12 class Component
13 {
14 public:
15     //純虛函數,只提供接口,沒有默認的實現
16     virtual void Operation()=0;    
17 
18     // 虛函數,提供接口,有默認的實現就是什么都不做
19     virtual void Add(Component*);
20     virtual void Remove(Component*);
21     virtual Component* GetChild(int index);
22     virtual ~Component();
23 protected:
24     Component();
25 };
26 
27 //Leaf是葉子結點,也就是不含有子組件的結點類,所以不用實現Add、Remove、GetChild等方法
28 class Leaf:public Component
29 {
30 public:
31     //只實現Operation接口
32     virtual void Operation();            
33     Leaf();
34     ~Leaf();
35 };
36 
37 //Composite:含有子組件的類
38 class Composite:public Component
39 {
40 public:
41     Composite();
42     ~Composite();
43     //實現所有接口
44     void Operation();
45     void Add(Component*);
46     void Remove(Component*);
47     Component* GetChild(int index);
48 private:
49     //這里采用vector來保存子組件
50     vector<Component*> m_ComVec;        
51 };
52 #endif

Compostie.cpp

 1 #include "Composite.h"
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 Component::Component()
 7 {}
 8 
 9 Component::~Component()
10 {}
11 
12 void Component::Add(Component* com)
13 {
14     cout << "add" << endl;
15 }
16 
17 void Component::Remove(Component* com)
18 {
19 }
20 
21 void Component::Operation()
22 {
23     cout << "Component::Operation" << endl;
24 }
25 
26 Component* Component::GetChild(int index)
27 {
28     return NULL;
29 }
30 
31 
32 Leaf::Leaf()
33 {}
34 
35 Leaf::~Leaf()
36 {}
37 
38 void Leaf::Operation()
39 {
40     cout<< "Leaf::Operation" <<endl;
41 }
42 
43 Composite::Composite()
44 {
45 }
46 
47 Composite::~Composite()
48 {}
49 
50 void Composite::Add(Component* com)
51 {
52     this->m_ComVec.push_back(com);
53 }
54 
55 void Composite::Remove(Component* com)
56 {
57     this->m_ComVec.erase(&com);
58 }
59 
60 void Composite::Operation()
61 {
62     cout << "Composite::Operation" << endl;
63     vector<Component*>::iterator iter = this->m_ComVec.begin();
64     for(;iter!= this->m_ComVec.end();iter++)
65     {
66         (*iter)->Operation();
67     }
68 }
69 
70 Component* Composite::GetChild(int index)
71 {
72     if(index < 0 || index > this->m_ComVec.size())
73     {
74         return NULL;
75     }
76     return this->m_ComVec[index];
77 }

main.cpp

 1 #include "Composite.h"
 2 #include <iostream>
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {
 8     /*
 9       不管是葉子Leaf還是Composite對象pRoot、pCom都實現了Operation接口,所以可以一致對待,直接調用Operation()
10       體現了“使得用戶對單個對象和組合對象的使用具有一致性。”
11     */
12     Composite* pRoot = new Composite();
13 
14     //組合對象添加葉子節點
15     pRoot->Add(new Leaf());
16 
17     Leaf* pLeaf1 = new Leaf();
18     Leaf* pLeaf2 = new Leaf();
19 
20     //這里的葉子再添加葉子是沒有意義的。
21     //由於葉子與組合對象繼承了相同的接口,所以語法上是對的,實際上什么也沒做(繼承自基類Component的Add方法)。
22     //葉子節點只實現了Operation方法,其他Add、Remove、GetChild都繼承自基類,沒有實際意義。
23     pLeaf1->Add(pLeaf2);
24     pLeaf1->Remove(pLeaf2);
25     //執行葉子Operation操作
26     pLeaf1->Operation();
27 
28     //組合對象實現了基類Component的所有接口,所以可以做各種操作(Add、Remove、GetChild、Operation)。
29     Composite* pCom = new Composite();
30     //組合對象添加葉子節點
31     pCom->Add(pLeaf1);
32     //組合對象添加葉子節點
33     pCom->Add(pLeaf2);
34     //執行組合對象Operation操作
35     pCom->Operation();
36 
37     //組合對象添加組合對象
38     pRoot->Add(pCom);
39 
40     //執行組合對象Operation操作
41     pRoot->Operation();
42 
43     //Component* cp = pCom->GetChild(0);
44     //cp->Operation();
45 
46     //pCom->Remove(pLeaf1);
47 
48     return 0;
49 }

組合的另一個例子:摘自http://blog.csdn.net/wuzhekai1985/article/details/6667564

      DP書上給出的定義:將對象組合成樹形結構以表示“部分-整體”的層次結構。組合使得用戶對單個對象和組合對象的使用具有一致性。注意兩個字“樹形”。這種樹形結構在現實生活中隨處可見,比如一個集團公司,它有一個母公司,下設很多家子公司。不管是母公司還是子公司,都有各自直屬的財務部、人力資源部、銷售部等。對於母公司來說,不論是子公司,還是直屬的財務部、人力資源部,都是它的部門。整個公司的部門拓撲圖就是一個樹形結構。

      下面給出組合模式的UML圖。從圖中可以看到,FinanceDepartment、HRDepartment兩個類作為葉結點,因此沒有定義添加函數。而ConcreteCompany類可以作為中間結點,所以可以有添加函數。那么怎么添加呢?這個類中定義了一個鏈表,用來放添加的元素。

相應的代碼實現為:

 1 class Company  
 2 {
 3 public:
 4     Company(string name) { m_name = name; }
 5     virtual ~Company(){}
 6     virtual void Add(Company *pCom){}
 7     virtual void Show(int depth) {}
 8 protected:
 9     string m_name;
10 };
11 //具體公司
12 class ConcreteCompany : public Company  
13 {
14 public:
15     ConcreteCompany(string name): Company(name) {}
16     virtual ~ConcreteCompany() {}
17     void Add(Company *pCom) { m_listCompany.push_back(pCom); } //位於樹的中間,可以增加子樹
18     void Show(int depth)
19     {
20         for(int i = 0;i < depth; i++)
21             cout<<"-";
22         cout<<m_name<<endl;
23         list<Company *>::iterator iter=m_listCompany.begin();
24         for(; iter != m_listCompany.end(); iter++) //顯示下層結點
25             (*iter)->Show(depth + 2);
26     }
27 private:
28     list<Company *> m_listCompany;
29 };
30 //具體的部門,財務部
31 class FinanceDepartment : public Company 
32 {
33 public:
34     FinanceDepartment(string name):Company(name){}
35     virtual ~FinanceDepartment() {}
36     virtual void Show(int depth) //只需顯示,無限添加函數,因為已是葉結點
37     {
38         for(int i = 0; i < depth; i++)
39             cout<<"-";
40         cout<<m_name<<endl;
41     }
42 };
43 //具體的部門,人力資源部
44 class HRDepartment :public Company  
45 {
46 public:
47     HRDepartment(string name):Company(name){}
48     virtual ~HRDepartment() {}
49     virtual void Show(int depth) //只需顯示,無限添加函數,因為已是葉結點
50     {
51         for(int i = 0; i < depth; i++)
52             cout<<"-";
53         cout<<m_name<<endl;
54     }
55 };

客戶使用方式:

 1 int main()
 2 {
 3     Company *root = new ConcreteCompany("總公司");
 4     Company *leaf1=new FinanceDepartment("財務部");
 5     Company *leaf2=new HRDepartment("人力資源部");
 6     root->Add(leaf1);
 7     root->Add(leaf2);
 8 
 9     //分公司A
10     Company *mid1 = new ConcreteCompany("分公司A");
11     Company *leaf3=new FinanceDepartment("財務部");
12     Company *leaf4=new HRDepartment("人力資源部");
13     mid1->Add(leaf3);
14     mid1->Add(leaf4);
15     root->Add(mid1);
16     //分公司B
17     Company *mid2=new ConcreteCompany("分公司B");
18     FinanceDepartment *leaf5=new FinanceDepartment("財務部");
19     HRDepartment *leaf6=new HRDepartment("人力資源部");
20     mid2->Add(leaf5);
21     mid2->Add(leaf6);
22     root->Add(mid2);
23     root->Show(0);
24 
25     delete leaf1; delete leaf2;
26     delete leaf3; delete leaf4;
27     delete leaf5; delete leaf6;    
28     delete mid1; delete mid2;
29     delete root;
30     return 0;
31 }

上面的實現方式有缺點,就是內存的釋放不好,需要客戶自己動手,非常不方便。有待改進,比較好的做法是讓ConcreteCompany類來釋放。因為所有的指針都是存在ConcreteCompany類的鏈表中。C++的麻煩,沒有垃圾回收機制。上面的實現方式有缺點,就是內存的釋放不好,需要客戶自己動手,非常不方便。有待改進,比較好的做法是讓ConcreteCompany類來釋放。因為所有的指針都是存在ConcreteCompany類的鏈表中。C++的麻煩,沒有垃圾回收機制。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM