爲方便讀者,本文已添加至索引:html
最近時間比較緊,因此發文的速度相對較慢了。可是看到園子裏有不少朋友對設計模式感興趣,我感受很高興,可以和你們一塊兒學習這些知識。算法
以前的文章中,咱們已經介紹了對象建立型和對象結構型的設計模式(請參見索引)。從本篇開始,咱們將接觸的是對象行爲型設計模式。所謂行爲模式涉及到算法和對象間職責的分配。在對象和類的模式以外,還涉及了它們之間的通訊模式。好比咱們今次的主題:Chain of Responsibility(職責鏈)模式,它就描述了一種請求的傳遞和響應機制。設計模式
爲了能直觀地理解職責鏈模式,咱們將繼續採用要點梳理和示例講解的方式進行。首先來回顧下該系列筆記的主要人物設定(參見Abstract Factory筆記):七個小霍比特人和白雪公主。耳熟能詳的故事背景我就很少說了,白雪公主是逃到森林裏的。森林裏的生活,對於白雪公主而言是艱辛的,她老是得依靠小霍比特人的幫助和照料。七位Hobbits各有所長,譬如會作美味食物的theCook,睿智博學的theWise(參見Adapter模式筆記),勇敢善戰的theWarrior(參見Factory Method模式筆記)等等。學習
這和咱們的職責鏈有何關係?當咱們可愛的白雪公主須要小霍比特人幫助的時候,她可能並不知道誰最終能幫到她。職責鏈模式正好提供了一種對應的解決方式。它經過給多個對象處理一個請求的機會,從而將提交請求的對象與可能提供解決方案的對象解耦。讓咱們舉個簡單的例子,在森林小屋裏白雪公主睡得不踏實,她的牀過小了,她想要大一點的牀。因而她找到了theWise,向他提出了這個請求。theWise接收了請求,可是他處理不了。因此他接着把公主的請求轉達給theWarrior。但theWarrior仍是解決不了,因此他又找來了theWoodCutter(參見Bridge模式筆記)。因而theWoodCutter跑到森林裏砍了一大堆木頭回來,作了一張很大的牀給公主睡。this
在上面所說的例子中,每一個小霍比特人都可以接收公主的請求,而且每一個人都有一個最親密的夥伴(後繼者)。若是他發現本身處理不了這個請求時,他只需簡單地把請求轉達給親密的小夥伴(後繼者)便可。能夠認爲,公主的請求在一條霍比特人的關係鏈上進行傳遞,直至其中一個對象把它處理掉。從鏈中第一個對象開始,收到的請求要麼親自處理它,要麼轉發給鏈中的下一個候選者。提交請求的對象並不明確地知道哪個對象將會處理它,咱們稱該請求有一個隱式的接收者(implicit receiver)。spa
爲了進一步理解職責鏈模式,咱們進行一些要點梳理。設計
一種典型的對象結構code
瞭解了基本概念以後,讓咱們回到前言中所講的例子。下面將用C++代碼來模擬實現其中的職責鏈。首先,Hobbit類是小霍比特人們的基類:htm
1 #define ABILITY_COOK 1 2 #define ABILITY_FIGHT 2 3 #define ABILITY_CARP 3 4 #define ABILITY_KNOWLEDGE 4 5 // ... other ability types ... 6 7 class Hobbit { 8 public: 9 Hobbit(Hobbit* h = 0, int a = 0); 10 11 virtual bool hasAbility(int); 12 virtual void handleRequest(int); 13 private: 14 Hobbit* _successor; // the intimate friend. 15 int _ability; 16 };
它定義了處理請求的接口handleRequest,同時也保持了對象鏈中它的後繼者的引用_successor(也就是前文所指的最親密的小夥伴)。它還維護一個_ability,表明這個小霍比特人的最擅長能力。handleRequest是最關鍵的操做,它能夠被子類重定義,這裏默認是傳遞給後繼者去處理。hasAbility則是一個輔助操做,用於判斷對象是否具有相應的能力。對象
1 Hobbit::Hobbit(Hobbit* h, int a): _successor(h), _ability(a) {} 2 3 bool Hobbit::hasAbility(int a) { 4 return _ability == a; 5 } 6 7 void Hobbit::handleRequest() { 8 if (_successor !=0) { 9 _successor->handleRequest(); 10 } 11 }
theWarrior是HobbitWarrior類的實例對象,而HobbitWarrior則是Hobbit的子類:
1 class HobbitWarrior : public Hobbit { 2 protected: 3 HobbitWarrior(Hobbit* h, int a = ABILITY_FIGHT); 4 5 virtual void handleRequest(int); 6 } 7 8 HobbitWarrior::HobbitWarrior(Hobbit* h, int a):Hobbit(h, a) {} 9 10 HobbitWarrior::handleRequest(int a) { 11 if (hasAbility(a)) { 12 // handle this request. 13 } else { 14 Hobbit::handleRequest(a); 15 } 16 }
在這裏,HobbitWarrior版本的handleRequest首先會檢測本身是否具有該能力,若是沒有的話,它將把這個請求轉發給後繼者。同理,咱們有很是相似的HobbitWoodCutter類:
1 class HobbitWoodCutter : public Hobbit { 2 protected: 3 HobbitWoodCutter(Hobbit* h, int a = ABILITY_CARP); 4 5 virtual void handleRequest(int); 6 } 7 8 HobbitWoodCutter::HobbitWoodCutter(Hobbit* h, int a):Hobbit(h, a) {} 9 10 HobbitWoodCutter::handleRequest(int a) { 11 if (hasAbility(a)) { 12 // handle this request. 13 } else { 14 Hobbit::handleRequest(a); 15 } 16 }
同理還有HobbitWise類等等,下面咱們就建立並鏈接這些對象以演示前言部分的例子:
1 HobbitWoodCutter* theWoodCutter = new HobbitWoodCutter(0); 2 HobbitWarrior* theWarrior = new HobbitWarrior(theWoodCutter); 3 HobbitWise* theWise = new HobbitWise(theWarrior);
而後白雪公主找到theWise,向他請求一個木工活:
theWise->handleRequest(ABILITY_CARP);
後面發生的事,你們就都知道了。對應於這個例子的UML圖以下:
從例子咱們能夠看出,白雪公主無需知道小霍比特人之間的聯繫,她只須要知道,她的請求會獲得應有的響應(除了完成請求以外,固然也有可能你們都完不成這個請求)。
用專業的術語表示,職責鏈模式具備以下優缺點:
職責鏈模式是一個看起來簡單,可是卻容易被誤解的模式。最多見的誤區就是上下級關係這塊。事實上職責鏈並無嚴格的上下級關係,只是保持一個對後繼者的引用而已。後繼者能夠是它父類的對象,也能夠是同級的對象。
今天的筆記就到這裏了,歡迎你們批評指正!若是以爲能夠的話,好文推薦一下,我會很是感謝的!