問題聚焦:算法
行爲模式,將你的注意力從控制流轉移到對象間的聯繫通訊方式上來。
本節所介紹的職責鏈,是典型的鬆耦合設計模式。
職責鏈模式能夠相應多個請求接受者,選擇其中最合適的處理者。
行爲模式:
定義:算法和對象間職責的分配,不只描述對象或類的模式,還描述它們之間的通訊模式。
使用繼承機制:使用繼承機制在類間分派行爲。如Tempalte Method和Interpreter。
使用對象複合:一些行爲對象模式描述了一組對等的對象怎樣相互協做以完成其中任一個對象都沒法單獨完成的任務。其要注意的問題是如何低耦合又保持相互的瞭解。如Mediator模式提供的鬆耦合所需的間接性。
職責鏈(Chain of Responsibility)經過一條候選對象鏈式向一個對象發送請求。
觀察者模式(Oberserver)定義並保持對象間的依賴關係。
其餘的行爲對象模式常將行爲封裝在一個對象中並將請求指派給它。
職責鏈(Chain Of Responsibility)
意圖:
使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係。
將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
動機:
依然以一個實際的例子說明該模式的設計出發點。
Demo:
考慮一個圖形用戶界面中的幫助機制設計
需求:用戶在界面的任一部分上點擊就能夠獲得幫助信息,所提供的幫助依賴於點擊的是界面的哪一部分以及其上下文。
若是對那一部分界面沒有特定的幫助信息,那麼幫助系統應該顯示一個關於當前上下文的較通常的幫助信息。好比,整個對話框的幫助信息。
思路:從特殊到廣泛的順序組織幫助信息,有一個對象專門負責選擇幫助請求。
挑戰:提交幫助請求的對象並不明確知道誰是最終提供幫助的對象。咱們要作的是將提交幫助請求的對象與可能提供幫助信息的對象解耦和。
給多個對象處理一個請求的機會,挑選最適合的處理者,從而解耦發送者和接受者。該請求將按對象鏈傳遞,知道一個合適的請求接受者對它做出反應。
以下圖所示:
在PrintButton和OKButton接收到的請求並不直接處理,而是沿轉發鏈向下轉發至PrintDialog,一直到Application,Application處理它或忽略它。
效果就是,提交請求的客戶不直接引用最終相應它的對象。
實現:要沿鏈轉發請求,並保證接收者爲隱式的(implicit),每一個鏈上的對象都有一致的處理請求和訪問鏈上後繼者的接口。
設計:HelpHandler類,做爲全部候選對象類的父類,用HelpHandler定義的接口處理幫助請求,或者下發請求。
HandleHelp操做缺省的是將請求轉發給後繼,子類可重定義這一操做以在適當的狀況下提供幫助;不然它們可以使用缺省實現轉發該請求。
適用性:
如下狀況使用Responsibility鏈:
- 有多個的對象能夠處理一個請求,哪一個對象處理該請求,在運行時刻自動肯定。
- 你想在不明確指定接受者的狀況下,向多個對象中的一個,提交一個請求。
- 可處理一個請求的對象集合應被動態指定。
結構:
一個典型的對象結構可能以下圖所示:
參與者:
- Handler(HelpHandler):
- Concretehandler(PrintButton):
- 處理它負責的請求
- 可訪問它的後繼者
- 若是可處理該請求,就處理;不然將該請求轉發給它的後繼者
- Client:向鏈上的具體處理者對象提交請求
協做:
當客戶提交一個請求時,請求沿鏈傳遞直至有一個ConcreteHandler對象負責處理它。
效果:
優勢和缺點
- 下降耦合度:請求提交者無需知道哪個對象處理請求;請求接受者不須要知道鏈的存在,它只須要知道它的後繼是誰。
- 加強了給對象指派職責的靈活度
- 不保證被接受:當請求到達鏈尾仍沒有被處理時,就得不處處理。
實現:
下面是在職責鏈模式中要考慮的實現問題:
- 實現後繼者鏈(接受請求者組成的處理請求鏈):
- 鏈接後繼者:若是沒有一個可以使用的引用定義一個鏈,那麼Handler不只定義該請求的接口,一般也維護後繼連接。這時,Concretehandler子類對該請求不感興趣時,進行無條件轉發。
- 表示請求:三種方式表示請求
- 簡單地,硬編碼操做調用,缺點,只能轉發定義的固定的一組請求
- 使用處理函數,接受一個請求碼。
- 使用獨立的請求對象請求參數。
代碼示例:
下面之前面所述的在線幫助系統,來演示職責鏈是如何處理請求的。
類設定:
HelpHandler類定義了處理幫助請求的接口。
職責:維護一個幫助主題;保持對幫助處理對象鏈中的後繼者的引用
關鍵操做:HanldeHelp,可被子類重定義;HasHelp輔助操做,用於檢查是否有一個相關的幫助主題。
typedef int Topic;
const Topic NO_HELP_TOPIC = -1;
class HelpHandler {
public:
HelpHanlder(HelpHandler* = 0, Topic = NO_HELP_TOPIC);
virtual bool HasHelp();
virtual void SetHandler(HelpHandler*, Topic);
virtual void HandleHelp();
private:
HelpHandler* _successor;
Topic _topic;
};
HelpHandler::HelpHandler(HelpHandler* h, Topic t)
: _successor(h), _topic(t) {}
HelpHandler::HandlerHelp () {
if (_successor != 0) {
_successor->HandleHelp();
}
}
Widget抽象類:全部窗口組件的父類
class Widget : public HelpHandler {
protected:
Widget(Widget* parent, Topic t = NO_HELP_TOPIC);
private:
Widget* _parent;
};
Widget::Widget (Widget* w, Topic t) : HelpHandler(w, t) {
_parent = w;
}
按鈕是鏈上的第一個處理者
class Button : public Widget {
public:
Button(Widget* d, Topic t = NO_HELP_TOPIC);
virtual void HandleHelp();
};
Button::Button (Widget* h , Topic t) : Widget(h, t) { }
void Button::HandleHelp () {
if (HasHelp()) {
// offer help on the request
}
else {
HelpHandler::HandleHelp();
}
}
Dialog實現了一個相似的策略。其後繼者不只能夠是一個窗口組件,還能夠是任意的幫助請求處理對象。
class Dialog : public Widget {
public:
Dialog(HelpHandler* h, Topic t = NO_HELP_TOPIC);
virtual void HandleHelp();
};
Dialog::Dialog(HelpHandler* h, Topic t) : Widget(0) {
SetHandler(h, t);
}
void Dialog::HandleHelp() {
if (HasHelp()) {
// offer help
}
else {
HelpHandler::HandleHelp();
}
}
在鏈的末端是Application的一個實例。
class Application : public HelpHandler {
......
};
類設定完畢。
下面的代碼建立並鏈接這些對象。
const Topic PRINT_TOPIC = 1;
const Topic PAPER_ORIENTATION_TOPIC = 2;
const Topic APPLICATION_TOPIC = 3;
Application* application = new Application(APPLICATION_TOPIC);
Dialog* dialog = new Dialog(application, PRINT_TOPIC);
Button* button = new Button(dialog, PAPER_ORIENTATION_TOPIC);
咱們可對鏈上的任意對象調用HandleHelp以觸發相應的幫助請求。
相關模式:
職責鏈模式常與Composite模式一塊兒使用,這種狀況下,一個構件的父構件能夠做爲它的後繼。
參考資料:
《設計模式:可複用面向對象軟件的基礎》