C++設計模式——職責鏈模式( Chain of Responsibility Pattern)

leonardohaig發表於2020-12-13

C++設計模式——職責鏈模式( Chain of Responsibility Pattern)

目錄

定義

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain untill an object handle it.
使多個物件都有機會處理請求,從而避免了請求的傳送者和接受者之間的耦合關係。將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有物件處理它為止。

職責鏈模式(Chain of Responsibility Pattern):為了避免將一個請求的傳送者與接受者耦合在一起,讓多個物件都有機會處理請求。將接受請求的物件接成一條鏈,當有請求發生時,可以沿著這條鏈傳遞請求,直到有一個物件能夠處理它為止。

這個過程實際上就是一個遞迴呼叫

職責鏈可以是一條直線,一個環或者樹形結構,常見的是直線型,即沿著一條單向的鏈來傳遞請求。客戶無需關心請求的處理細節以及請求的傳遞,只需將請求傳送到鏈上。將請求的傳送者和處理者解耦,是職責鏈模式的動機。

職責鏈模式是一種物件行為型模式,在這種模式中,通常每個接收者都包含對另一個接收者的引用。如果一個物件不能處理該請求,那麼它會把相同的請求傳給下一個接收者,依此類推。

要點主要是:
1.有多個物件共同對一個任務進行處理;
2.這些物件使用鏈式儲存結構,形成一個鏈,每個物件知道自己的下一個物件;
3.一個物件對任務進行處理,可以新增一些操作後將物件傳遞個下一個任務。也可以在此物件上結束任務的處理,並結束任務;
4.客戶端負責組裝鏈式結構,但是客戶端不需要關心最終是誰來處理了任務;

職責鏈模式主要包含以下角色:

  • 抽象處理者(Handler)角色:它定義了一個處理請求的介面,一般設計為抽象類,由於不同的具體處理者處理請求的方式不同,因此在其中定義了抽象請求處理方法。每一個處理者的下家還是一個處理者,故在抽象處理者中定義了一個抽象處理者型別的物件作為其對下家的引用,通過該引用,處理者可以連成一條鏈。如果需要,介面可以定義出一個方法以設定和返回對下家的引用;
  • 具體處理者(ConcreteHandler)角色:它是抽象處理者的子類,可以處理使用者請求,實現了抽象處理方法。處理之前要進行判斷,看看是否有相應的處理許可權,若有則處理請求,若無則轉發請求給下家。

    客戶類(Client)角色:建立處理鏈,並向鏈頭的具體處理者物件提交請求,它不關心處理細節和請求的傳遞過程

其結構圖如下所示:
職責鏈模式結構圖
一個典型的職責鏈如下圖所示:
客戶端設定職責鏈方式

程式碼示例

#include <bits/stdc++.h>

//
//職責鏈模式(Chain of Responsibility Pattern)
//關鍵程式碼:Handler內指明其上級,handleRequest()裡判斷是否合適,不合適則傳遞給更高一級處理者


//定義處理請示的介面
//抽象類
class Handler {
protected:
    std::shared_ptr<Handler> m_pNextHandler;
public:
    Handler() : m_pNextHandler(nullptr) {}

    Handler(const std::shared_ptr<Handler> pNextHandler) : m_pNextHandler(pNextHandler) {}

    virtual ~Handler() = default;

    //設定下一個執行者/更高一級的執行者
    void SetNextHandler(const std::shared_ptr<Handler> pNextHandler) {
        m_pNextHandler = pNextHandler;
    }

    virtual void HandleRequest(int request) = 0;

};

//具體處理類,處理負責的請求,無法處理就丟給後繼者
//鏈上的第1個節點
class ConcreteHandler1 : public Handler {
public:
    ConcreteHandler1() {}

    ConcreteHandler1(const std::shared_ptr<Handler> pNextHandler) : Handler(pNextHandler) {}

    void HandleRequest(int request) override {
        std::cout << "節點1收到請求" << std::endl;
        if (request >= 0 && request < 10)
            std::cout << "節點1處理請求 " << request << std::endl;
        else if (m_pNextHandler) {
            std::cout << "我是節點1,這個事情我處理不了,得交給上一級" << std::endl;
            m_pNextHandler->HandleRequest(request);
        }

    }
};

//具體處理類,處理負責的請求,無法處理就丟給後繼者
//鏈上的第2個節點
class ConcreteHandler2 : public Handler {
public:
    ConcreteHandler2() {}

    ConcreteHandler2(const std::shared_ptr<Handler> pNextHandler) : Handler(pNextHandler) {}

    void HandleRequest(int request) override {
        std::cout << "節點2收到請求" << std::endl;
        if (request >= 10 && request < 20)
            std::cout << "節點2處理請求 " << request << std::endl;
        else if (m_pNextHandler) {
            std::cout << "我是節點2,這個事情我處理不了,得交給上一級" << std::endl;
            m_pNextHandler->HandleRequest(request);
        }

    }
};

//具體處理類,處理負責的請求,無法處理就丟給後繼者
//鏈上的第3個節點
class ConcreteHandler3 : public Handler {
public:
    ConcreteHandler3() {}

    ConcreteHandler3(const std::shared_ptr<Handler> pNextHandler) : Handler(pNextHandler) {}

    void HandleRequest(int request) override {
        std::cout << "節點3收到請求" << std::endl;
        if (request >= 20)
            std::cout << "節點3處理請求 " << request << std::endl;
        else if (m_pNextHandler) {
            std::cout << "我是節點3,這個事情我處理不了,得交給上一級" << std::endl;
            m_pNextHandler->HandleRequest(request);

        } else {
            std::cout << "我是節點3,這個事情沒有節點可以處理" << std::endl;
        }
    }
};

int main() {
    std::shared_ptr<Handler> pHandle_3 = std::make_shared<ConcreteHandler3>();
    std::shared_ptr<Handler> pHandle_2 = std::make_shared<ConcreteHandler2>(pHandle_3);
    std::shared_ptr<Handler> pHandle_1 = std::make_shared<ConcreteHandler1>();//首節點
    pHandle_1->SetNextHandler(pHandle_2);

    int request[] = {2, 5, 14, 44, 22, 15, 23, 12, -5};

    //客戶端將請求交給首節點,然後請求即可在鏈上傳遞
    for (auto i : request) {
        pHandle_1->HandleRequest(i);
        std::cout << std::string(30, '-') << std::endl;
    }

    return 0;
    //執行結果:
    //節點1收到請求
    //節點1處理請求 2
    //------------------------------
    //節點1收到請求
    //節點1處理請求 5
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //節點2處理請求 14
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //我是節點2,這個事情我處理不了,得交給上一級
    //節點3收到請求
    //節點3處理請求 44
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //我是節點2,這個事情我處理不了,得交給上一級
    //節點3收到請求
    //節點3處理請求 22
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //節點2處理請求 15
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //我是節點2,這個事情我處理不了,得交給上一級
    //節點3收到請求
    //節點3處理請求 23
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //節點2處理請求 12
    //------------------------------
    //節點1收到請求
    //我是節點1,這個事情我處理不了,得交給上一級
    //節點2收到請求
    //我是節點2,這個事情我處理不了,得交給上一級
    //節點3收到請求
    //我是節點3,這個事情沒有節點可以處理
    //------------------------------
}

總結

  • 優缺點
    • 優點
      1.請求者和接受者鬆散耦合:一個物件無須知道是其他哪一個物件處理了其請求,僅需知道該請求會被處理即可,接受者和傳送者都沒有對方的明確資訊,每個職責物件只負責自己的職責範圍,鏈中物件不需要知道鏈的結構,各個元件間完全解耦,由客戶端負責鏈的建立;
      2.動態組合:職責鏈模式會把功能分散到單獨的職責物件中,然後在使用時動態的組合形成鏈,可以通過在執行時對該鏈進行動態的增加或修改來增加或改變處理一個請求的職責。
      3.在系統中增加處理者無需修改原有程式碼,只需在客戶端重新建鏈即可,符合開閉原則;
      4.責任分擔:每個類只需要處理自己該處理的工作,不該處理的傳遞給下一個物件完成,明確各類的責任範圍,符合類的單一職責原則。
    • 缺點
      1.由於一個請求沒有明確的接受者,那麼就不能保證它一定會被處理,可能一直到鏈的末端都得不到處理;一個請求也可能因為職責鏈沒有建立正確而到不到處理;
      2.對於較長的職責鏈,請求處理時需要很多的職責物件,系統效能將受影響,除錯也不方便;
      3.建鏈不當,可能造成迴圈呼叫,系統陷入死迴圈。
  • 適用場景   
    1.有多個物件可以處理同一個請求,具體哪個物件處理該請求待執行時刻再確定;
    2.在不明確指定接受者的情況下,向多個物件中的一個提交一個請求;
    3.可動態指定一組物件處理請求,客戶端可以動態建立職責鏈來處理請求。

對於職責鏈中的一個處理者物件,有兩個行為。一是處理請求,二是將請求傳遞到下一節點,不允許某個處理者物件在處理了請求後又將請求傳送給上一個節點的情況。

對於職責鏈來說,可以分為兩種情況:純的職責鏈模式和不純的職責鏈模式

純的職責鏈模式要求一個具體的處理者物件只能在兩個行為中選擇一個:要麼承擔全部責任,要麼將責任推給下家,不允許出現某一個具體處理者承擔了一部分或者全部責任後又將責任向下傳遞。而且一個請求必須被某一個處理者所接受,不能出現某個請求未被任何一個處理者物件處理的情況。

不純的職責鏈模式,允許某個請求被一個具體處理者部分處理後向下傳遞,或者一個具體處理者處理完某個請求後其後續處理者可以繼續處理該請求,而且一個請求可以最終不被任何處理者物件接受並處理。

參考資料

1.職責鏈模式
2.責任鏈模式(職責鏈模式)詳解

相關文章