[學習筆記]設計模式之Composite

爲方便讀者,本文已添加至索引:html

寫在前面

Composite(組合)模式中,用戶可使用多個簡單的組件以造成較大的組件,而這些組件還可能進一步組合成更大的。它重要的特性是可以讓用戶一致地對待單個對象和組合對象。不知你們是否還記得女巫格琳達(見筆記Facade模式),她的小屋經營得很順利,給小夥伴們的生活帶來了極大地便利。今天,她又推出了一項全新的銷售項目,那就是「私人訂製自主行動型魔法小人偶-I」。乍看之下是個稻草人模樣,但其實客人們可以經過本身訂製小人偶的不一樣部件,組裝成一個能夠按照預先設定的指望目標而自主行動的魔法人偶。他們能夠幫忙播種,耕地,烹飪,甚至戰鬥等等等等。設計模式

女巫格琳達在設計這個小人偶原型時,採用了Composite模式從而簡化了客戶們的操做,同時也能利用清晰的樹形結構來展現訂製的所有內容。在咱們進入到示例部分講解小人偶相關代碼以前,先來具體瞭解下Composite模式的基本知識:函數

要點梳理

  • 目的分類
    • 對象結構型模式
  • 範圍準則
    • 對象(該模式處理對象間的關係,這些關係在運行時刻是能夠變化的,更具動態性)
  • 主要功能
    • 將對象組合成樹形結構以表示「部分-總體」的層次結構。這可使得用戶在使用單個對象和組合對象時有一致性。
  • 適用狀況
    • 當咱們想表示對象的部分-總體層次結構時;
    • 當咱們但願用戶忽略組合對象與單個對象的不一樣,讓他們可以統一地去使用時
  • 參與部分
    • Component:爲組合中的對象聲明接口;或者在適當的狀況下,實現全部類共有接口的缺省行爲;聲明一個接口用於訪問和管理Component的子組件;在遞歸結構中定義一個接口,用於訪問一個父部件,並在合適的狀況下實現它,固然這個是可選的。
    • Leaf:在組合中表示葉節點對象,須要說明的是,葉節點沒有子節點。
    • Composite:定義有子部件的那些部件的行爲,同時存儲子部件,實現Component中與子部件有關的接口。
    • Client:經過Component接口,操縱組合部件的對象。
  • 協做過程
    • 用戶使用Component類接口與組合結構中的對象進行交互。若是接收者是一個葉節點,則直接處理請求。若是接收者是Composite, 它一般將請求發送給它的子部件,在轉發請求以前與/或以後可能執行一些輔助操做。
  • UML圖

示例分析 - 私人訂製魔法小人偶-I

從上文的UML圖中,能夠看到Composite模式描述瞭如何使用遞歸組合,使得用戶沒必要對單個類與組合類進行區別。而實現這一點的關鍵,在於Component抽象類,它既能表明單個組件,又能表明容器。工具

對於咱們的魔法小人偶-I型而言,咱們一樣定義一個Component類爲接下來全部的部件定義一個接口。性能

 1 class Component {
 2 public:
 3     virtual ~Component();
 4     
 5     const char* name() { return _name; }
 6     
 7     virtual int myFunction();    // power on the function of the component.
 8     virtual int myValue();    // count the value of the component.
 9     
10     virtual void add(Component*);
11     virtual void remove(Component*);
12     virtual Iterator<Component*>* createIterator();
13     
14 protected:
15     Component(const char*);
16     
17 private:
18     const char* _name;
19 };

咱們看到Component聲明瞭一些操做來返回一個部件的屬性,譬如說它的價格等。子類將爲指定的部件實現這些接口。而對於createIterator操做,它可以返回一個訪問它零件的Iterator,從而可以在管理零件的時候,進行遍歷。學習

Component類的子類有不少,咱們將不會所有講述(那會顯得很冗餘)。咱們首先看到的是Leaf型的組件,它們不是容器,好比裝在原型裏面的驅動裝置,穿在外面的衣服,戴在頭上的帽子,可使用的武器、工具等等。好比咱們Clothes類:spa

1 class Clothes : public Component {
2 public:
3     Clothes(const char*);
4     virtual ~Clothes();
5     
6     virtual int myFunction();
7     virtual int myValue();
8 };

另外一些是Composite型的組件,它們做爲容器可以包含其餘部件。這種組件的基類CompositeComponent設計

 1 class CompositeComponent : public Component {
 2     virtual ~CompositeComponent();
 3     
 4     virtual int myFunction();
 5     virtual int myValue();
 6     
 7     virtual void add(Component*);
 8     virtual void remove(Component*);
 9     virtual Iterator<Component*>* createIterator();
10     
11 protected:
12     CompositeComponent(const char*);
13     
14 private:
15     List<Component*> _component;
16 };

它爲訪問和管理子部件定義了一些操做。操做add/remove將從存儲在_component成員變量中的部件列表中插入/刪除部件。而做爲容器的myValue操做,經過使用createIterator,來累加子部件的價格。code

1 int CompositeComponent::myValue() {
2     Iterator<Component*>* iter = createIterator();
3     int total = 0;
4     for (iter->first(); !iter->isDone(); iter->next()) {
5         total += iter->current()->myValue();
6     }
7     delete iter;
8     return total;
9 }

CompositeComponent類的子類之一ProtoBody,是最基本的人偶外殼容器,格琳達能夠經過在裏面安置一些驅動器Drive,傳感器Sensor等來使得它得以正常運做。component

1 class ProtoBody : public CompositeComponent {
2 public:
3     ProtoBody(const char*);
4     virtual ~ProtoBody();
5     
6     virtual int myFunction();
7     virtual int myValue();
8 };

好啦,咱們還能夠定義一些類似的其餘容器,譬如戰鬥人偶的武器袋WeaponBag等等。咱們能夠簡單地來模擬訂製一個可以保衛領土的戰鬥機器人:

 1 ProtoBody* body = new ProtoBody("A strong and tough body");
 2 
 3 WeaponBag* bag = new WeaponBag("Weapon bag");
 4 bag->add(new Weapon("Sharpen Sword"));
 5 bag->add(new Shield("Hard Shield"));
 6 
 7 body->add(bag);
 8 body->add(new Drive("Fight For Honor!"));
 9 body->add(new Clothes("Cool Armor"));
10 
11 cout << "The total price is " << body->myValue() << endl;

對於咱們的設計能夠看下面這張圖:

特色總結

從上面咱們能夠看到,Composite模式有以下一些特色:

  1. Composite模式定義了包含基本對象和組合對象的類層次結構。基本對象能夠被組合成更復雜的組合對象,而這個組合對象又能夠被組合,這樣不斷的遞歸下去。客戶代碼中,任何用到基本對象的地方均可以使用組合對象。
  2. 簡化客戶代碼。客戶能夠一致地使用組合結構和單個對象。一般用戶不知道(也不關心)處理的是一個葉節點仍是一個組合組件。這就簡化了客戶代碼, 由於在定義組合的那些類中不須要寫一些充斥着選擇語句的函數。
  3. 使得更容易增長新類型的組件
  4. 使咱們的設計變得更加通常化

同時,咱們在實現的時候須要注意如下幾點:

  1. 顯式的父部件引用。這能夠簡化組合結構的遍歷和管理。
  2. 共享組件。通常來講這都頗有用,好比它能夠減小對存貯的需求。
  3. 最大化Component接口。由於它的目的是使得用戶不知道他們正在使用的究竟是Leaf仍是Composite,所以,Component應儘可能多定義一些公共操做。
  4. 注意子部件的排序問題。
  5. 使用高速緩衝存貯改善性能,特別是當咱們須要對組合進行頻繁的遍歷或查找。

寫在最後

今天的筆記就到這裏了,歡迎你們批評指正!若是以爲能夠的話,好文推薦一下,我會很是感謝的!

相關文章
相關標籤/搜索