一、概述
Chain of Responsibility(職責鏈,以下簡稱CoR)模式通過將多個對象串接成一條鏈(Chain),並沿着這條鏈傳遞上層應用傳來的請求,直到有一個對象處理它爲止,使得多個對象都有機會處理上層應用傳來的請求,從而避免請求的發送者和接收者之間的耦合關係。對於Chain中的各個對象,可以採用類似單向鏈表或雙向鏈表的結構,保存各自後繼或者前接元素的引用/指針來實現鏈接(緊密鏈接),也可以僅僅由各對象的Container保存這種邏輯上的鏈接關係,而各對象彼此間並不需要知曉Chain中的其它對象(鬆散鏈接)。
對於Chain的結構,一個鏈可以是一條線,一個樹(普通的樹,或者平衡樹、紅黑樹等),也可以是一個環,在使用中可以根據需要選擇。
二、結構
以下是緊密鏈接CoR模式的典型結構:
圖1:CoR模式類圖示意
上述類圖中包括如下角色:
抽象處理者(Handler)角色:定義出一個處理請求的接口。如果需要,接口可以定義出一個方法,以設定和返回對下家的引用。這個角色通常由一個抽象類或接口實現。
具體處理者(ConcreteHandler)角色:具體處理者接到請求後,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
三、應用
在以下情況下可以考慮使用CoR模式:
1、有多個的對象可以處理一個請求,哪個對象處理該請求運行時刻自動確定。
2、你想在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
3、可處理一個請求的對象集合應被動態指定。
四、優缺點
CoR模式使得發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織鏈和分配責任。從而可以很大程度地降低處理請求與處理對象,以及處理對象之間的耦合關係。
需要注意的時,CoR模式並不創建Responsibility Chain,Responsibility Chain的創建必須由系統的其它部分創建出來,即需要上層應用對Chain進行維護,同時,需要注意的是,在大多數情況下,Chain的構建應該遵循一定的規則,如由主到次,由特殊到普通(就好像在if..else if...else中,將較General的限制條件放在前面,可能使得較Rigorous的限制條件永遠得不到執行)。
五、舉例
MFC中的消息處理機制就是典型的CoR模式,它採用的是前面所說的鬆散鏈接的線狀Chain。下面是從jjhou的"MFC深入淺出"中拷貝下來的WM_COMMAND消息的處理流程圖:
由於作者要詳細討論內部的函數調用關係,所以有些內容是我們在討論CoR模式時不需要關注的。上圖對於不熟悉MFC的讀者可能有一些陌生,上圖中箭頭由右至左反映了類之間的繼承關係,對於討論CoR而言,若單從對象的角度考慮,只需要關注最右邊的幾個最終的Concrete類:CMyView、CMyDocument、CMyFrameWnd、CMyWinApp即可,當收到消息時,其處理工作由如下函數完成:
// in FRMWND.CPP(MFC 4.0)
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// pump through current view FIRST
CView* pView = GetActiveView();
if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
// in VIEWCORE.CPP(MFC 4.0)
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
// first pump through pane
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through document
BOOL bHandled = FALSE;
if (m_pDocument != NULL)
{
// special state for saving view before routing to document
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
CView* pOldRoutingView = pThreadState->m_pRoutingView;
pThreadState->m_pRoutingView = this;
bHandled = m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
pThreadState->m_pRoutingView = pOldRoutingView;
}
return bHandled;
}
由此,可以大致看出消息在系統內流動的過程(注意,這是主窗口CMainFrame收到消息時的處理過程,其它對象收到消息時的處理過程類似,關於MFC消息處理的更深入的介紹,請參閱<深入淺出MFC>)。
雖然採用CoR進行消息處理的機制在MFC中根深蒂固地存在着(MFC採用消息映射表屏蔽了內部的複雜處理,DefWindowProc等輔助機制更進一步讓整個消息處理顯得自然而簡單),但採用CoR進行消息處理在現代軟件設計中往往被認爲是低效的,而且,在Java等更爲純粹的OOP語言中採用類似的機制很不可行,以下是典型的基於CoR的消息處理:
import java.applet.Applet;
import java.awt.*;
public class MouseSensor extends Frame {
public static void main(String[] args) {
MouseSensor ms = new MouseSensor();
ms.setBounds(10,10,200,200);
ms.show();
}
public MouseSensor() {
setLayout(new BorderLayout());
add(new MouseSensorCanvas(), "Center");
}
}
class MouseSensorCanvas extends Canvas {
public boolean mouseUp(Event event, int x, int y) {
System.out.println("mouse up");
return true; // Event has been handled. Do not propagate to container.
}
public boolean mouseDown(Event event, int x, int y) {
System.out.println("mouse down");
return true; // Event has been handled. Do not propagate to container.
}
}
在Java中,採用CoR進行消息處理存在以下弊端:
1、基於CoR的消息處理不可避免需要進行類的繼承和重載,如,爲了給按鈕添加鼠標移動處理,必須通過派生新的按鈕子類來完成,而類似這樣的需求過於平常,因此會使我們的應用中包含過多的類(MFC處理機制不會引起這個問題,因爲對於MFC程序員而言,我們總是說:““窗體的鼠標移動到按鈕上時的消息處理”,而不是“窗體上按鈕的鼠標移動消息處理”);
2、消息處理與其它類的方法混雜在類的實現中,不便於管理;
3、有失靈活性,不便於消息處理的動態添加、刪除(雖然可以通過添加flag來封閉消息處理邏輯,但相信沒有人會認爲這是一種好辦法)。
基於以上諸多原因,Java設計者爲了維持語言本身的簡單性,堅決地淘汰了AWT中舊的基於CoR的消息處理機制,在引入新的界面方案javax.swing的同時,引入了新的基於Observer模式的消息處理機制。
以下是新的基於Observer模式的消息處理:
import java.awt.*;
import java.awt.event.*;
public class MouseSensor extends Frame {
public static void main(String[] args) {
MouseSensor ms = new MouseSensor();
ms.setBounds(10,10,200,200);
ms.show();
}
public MouseSensor() {
Canvas canvas = new Canvas();
canvas.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
System.out.println("mouse down");
}
public void mouseReleased(MouseEvent e) {
System.out.println("mouse up");
}
});
setLayout(new BorderLayout());
add(canvas, "Center");
}
}
與基於CoR的消息處理相比,基於Observer的消息處理由於將消息處理交給了單獨的Observer來處理,使得程序結構更清晰,而且高效(消息直接被對應的Observer處理,無需輪詢)。
但這不表示基於CoR的消息處理沒有存在的價值,基於CoR的消息處理可以極大程度上降低消息的發送者與接收者之間的耦合程度,同時,在有些情況下,Observer模式並不能替代CoR模式進行消息處理,如:
1、需要消息被多個接收者依次處理時,並且消息可能在處理的過程中被修改或者處理順序爲我們所關注時;
2、當消息沒有經過嚴格分類時,應用Observer模式會變得比較困難。
大衛的Design Patterns學習筆記13:Chain of Responsibility
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章
大衛的Design Patterns學習筆記20:State
一、概述State(狀態)模式用於把一個對象的內部狀態從對象中分離出來,形成單獨的狀態對象,所有與該狀態相關的行爲都放入該狀態對象中。一個對象可能處在
billdavid
2020-06-29 08:29:14
大衛的Design Patterns學習筆記22:Template Method
一、概述Template Method(模板方法)模式定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以
billdavid
2020-06-29 08:29:13
大衛的Design Patterns學習筆記15:Interpreter
一、概述Interpreter(解釋器)模式描述瞭如何爲簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。在這裏使用語言這個詞
billdavid
2020-06-29 08:29:12
一文搞懂什麼是面向對象編程思想與設計原則(深度)
谭小先
2020-04-24 21:18:43
大衛的Design Patterns學習筆記12:Proxy
billdavid
2020-02-24 21:30:48
大衛的Design Patterns學習筆記10:Flyweight
billdavid
2020-02-24 21:30:48
大衛的Design Patterns學習筆記05:Singleton
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記07:Bridge
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記24:後記
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記14:Command
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記23:Vistor
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記17:Mediator
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記18:Memento
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記06:Adapter
billdavid
2020-02-24 21:30:37
大衛的Design Patterns學習筆記11:Decorator
billdavid
2020-02-24 21:30:37