分享
 
 
 

大卫的Design Patterns学习笔记13:Chain of Responsibility

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

一、概述

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模式会变得比较困难。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有