[學習筆記]設計模式之Chain of Responsibility

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

寫在前面

最近時間比較緊,因此發文的速度相對較慢了。可是看到園子裏有不少朋友對設計模式感興趣,我感受很高興,可以和你們一塊兒學習這些知識。算法

以前的文章中,咱們已經介紹了對象建立型對象結構型的設計模式(請參見索引)。從本篇開始,咱們將接觸的是對象行爲型設計模式。所謂行爲模式涉及到算法和對象間職責的分配。在對象和類的模式以外,還涉及了它們之間的通訊模式。好比咱們今次的主題:Chain of Responsibility(職責鏈)模式,它就描述了一種請求的傳遞和響應機制。設計模式

爲了能直觀地理解職責鏈模式,咱們將繼續採用要點梳理和示例講解的方式進行。首先來回顧下該系列筆記的主要人物設定(參見Abstract Factory筆記):七個小霍比特人和白雪公主。耳熟能詳的故事背景我就很少說了,白雪公主是逃到森林裏的。森林裏的生活,對於白雪公主而言是艱辛的,她老是得依靠小霍比特人的幫助和照料。七位Hobbits各有所長,譬如會作美味食物的theCook,睿智博學的theWise(參見Adapter模式筆記),勇敢善戰的theWarrior(參見Factory Method模式筆記)等等。學習

這和咱們的職責鏈有何關係?當咱們可愛的白雪公主須要小霍比特人幫助的時候,她可能並不知道誰最終能幫到她。職責鏈模式正好提供了一種對應的解決方式。它經過給多個對象處理一個請求的機會,從而將提交請求的對象與可能提供解決方案的對象解耦。讓咱們舉個簡單的例子,在森林小屋裏白雪公主睡得不踏實,她的牀過小了,她想要大一點的牀。因而她找到了theWise,向他提出了這個請求。theWise接收了請求,可是他處理不了。因此他接着把公主的請求轉達給theWarrior。但theWarrior仍是解決不了,因此他又找來了theWoodCutter(參見Bridge模式筆記)。因而theWoodCutter跑到森林裏砍了一大堆木頭回來,作了一張很大的牀給公主睡。this

在上面所說的例子中,每一個小霍比特人都可以接收公主的請求,而且每一個人都有一個最親密的夥伴(後繼者)。若是他發現本身處理不了這個請求時,他只需簡單地把請求轉達給親密的小夥伴(後繼者)便可。能夠認爲,公主的請求在一條霍比特人的關係鏈上進行傳遞,直至其中一個對象把它處理掉。從鏈中第一個對象開始,收到的請求要麼親自處理它,要麼轉發給鏈中的下一個候選者。提交請求的對象並不明確地知道哪個對象將會處理它,咱們稱該請求有一個隱式的接收者(implicit receiver)spa

爲了進一步理解職責鏈模式,咱們進行一些要點梳理。設計

要點梳理

  • 目的分類
    • 對象行爲型模式
  • 範圍準則
    • 對象(該模式處理對象間的關係,這些關係在運行時刻是能夠變化的,更具動態性)
  • 主要功能
    • 使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
  • 適用狀況
    • 有多個的對象能夠處理一個請求,哪一個對象處理該請求運行時刻自動肯定。
    • 咱們想在不明確指定接收者的狀況下,向多個對象中的一個提交一個請求。
    • 可處理一個請求的對象集合應被動態指定。
  • 參與部分
    • Handler:定義一個處理請求的接口;(可選)實現後繼鏈
    • ConcreteHandler:處理它所負責的請求;可訪問它的後繼者;若是可處理該請求,就處理之;不然將該請求轉發給它的後繼者
    • Client:向鏈上的ConcreteHandler對象提交請求
  • 協做過程
    • 當客戶提交一個請求時,請求沿鏈傳遞直至有一個ConcreteHandler對象負責處理它。
  • UML圖

一種典型的對象結構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圖以下:

特色總結

從例子咱們能夠看出,白雪公主無需知道小霍比特人之間的聯繫,她只須要知道,她的請求會獲得應有的響應(除了完成請求以外,固然也有可能你們都完不成這個請求)。

用專業的術語表示,職責鏈模式具備以下優缺點:

  1. 下降耦合度。該模式使得一個對象無需知道是其餘哪個對象處理其請求。接收者和發送者都沒有對方的明確的信息,且鏈中的對象不需知道鏈的結構。這樣一來,職責鏈簡化了對象的相互鏈接。它們僅需保持一個指向其後繼者的引用,而不需保持它全部的候選接受者的引用。
  2. 加強了給對象指派職責Responsibility的靈活性。咱們能夠經過在運行時刻對該鏈進行動態的增長或修改來增長或改變處理一個請求的那些職責,這給咱們更大的靈活性。
  3. 缺點就是不保證被處理。既然一個請求沒有明確的接收者,那麼就不能保證它必定會被處理,該請求可能一直到鏈的末端都得不處處理。一個請求也可能因該鏈沒有被正確配置而得不處處理。

職責鏈模式是一個看起來簡單,可是卻容易被誤解的模式。最多見的誤區就是上下級關係這塊。事實上職責鏈並無嚴格的上下級關係,只是保持一個對後繼者的引用而已。後繼者能夠是它父類的對象,也能夠是同級的對象。

寫在最後

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

相關文章
相關標籤/搜索