5.1 CHAIN OF RESPONSIBILITY(职责链) — 对象行为型模式

1 意图

使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

2 动机

考虑一个图形用户界面中的上下文有关的帮助机制。用户在界面的任一部分上点击就可以得到帮助信息,所提供的帮助依赖于点击的是界面的哪一部分及其上下文。例如,对话框中的按钮的帮助信息就可能和主窗口中类似的按钮不同。如果对那一部分界面没有特定的帮助信息,那么帮助系统应该显示一个关于当前上下文的较一般的帮助信息——比如说,整个对话框。

因此很自然地,应根据普遍(generality)即从最特殊到最普遍的顺序来组织帮助信息。而且,很明显,在这些用户界面对象中会有一个对象来处理帮助请求 ;至于是哪一个对象则取决于上下文以及可用的帮助具体到何种程度。

这儿的问题是提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象。我们要有一种办法将提交帮助请求的对象与可能提供帮助信息的对象解耦 ( decouple)。Chain of Responsibility模式告诉我们应该怎么做。

这一模式的想法是,给多个对象处理一个请求的机会,从而解耦发送者和接受者。该请求沿对象链传递直至其中一个对象处理它,如下图所示。


image.png

从第一个对象开始,链中收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者。提交请求的对象并不明确地知道哪一个对象将会处理它—我们说该请求有一个隐式的接收者(implicit receiver)。

假设用户在一个标有 “Print” 的按钮窗口组件上单击帮助,而该按钮包含在一个PrintDialog的实例中,该实例知道它所属的应用对象 (见前面的对象框图 )。下面的交互框图(diagram) 说明了帮助请求怎样沿链传递:


image.png

在这个例子中,既不是 aPrintButton 也不是aPrintDialog 处理该请求 ;它一直被传递给anApplication,anApplication 处理它或忽略它。提交请求的客户不直接引用最终响应它的对象。

要沿链转发请求,并保证接收者为隐式的( implicit ),每个在链上的对象都有一致的处理请求和访问链上后继者的接口。例如,帮助系统可定义一个带有相应的HandleHelp 操作的HelpHandler类。HelpHandler 可为所有候选对象类的父类,或者它可被定义为一个混入(mixin)类。这样想处理帮助请求的类就可将HelpH andler 作为其一个父类,如下页上图所示。

按钮、对话框,和应用类都使用 HelpHandler 操作来处理帮助请求。 HelpHandler的HandleHelp 操作缺省的是将请求转发给后继。子类可重定义这一操作以在适当的情况下提供帮助;否则它们可使用缺省实现转发该请求。


image.png
3 适用性

在以下条件下适用Responsibility链:

  • 1 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定;
  • 2 你想在不明确指定接受者的情况下,向多个对象中的一个提交一个请求;
  • 3 可处理一个请求的对象集合应被动态指定。
4 结构
image.png

一个典型的对象结构可能如下图所示:


image.png
5 参与者
  • Handler(如HelpHandle)
    ——定义一个处理请求的接口
    ——(可选)实现后继链
  • ConcreteHandler(如PrintButton和PrintDialog)
    ——处理它所负责的请求;
    ——可访问它的后继者;
    ——如果可处理该请求,就处理之,否则将该请求转发给它的后继者;
  • Client
    ——向链上的具体处理者(ConcreteHandler)对象提交请求。
6 协作

当客户提交一个请求时,请求沿链传递直至有一个ConcreteHandler对象负责处理它。

7 效果

Responsibility链有下列优点和缺点:

  • 1 降低耦合度: 该模式使得一个对象无需知道是其他哪一个对象处理其请求。对象仅需
    知道该请求会被“正确”地处理。接收者和发送者都没有对方的明确的信息,且链中的对象
    不需知道链的结构。
    结果是,职责链可简化对象的相互连接。它们仅需保持一个指向其后继者的引用,而不
    需保持它所有的候选接受者的引用。

  • 2 增强了给对象指派职责( Responsibility)的灵活性:当在对象中分派职责时,职责链给你
    更多的灵活性。你可以通过在运行时刻对该链进行动态的增加或修改来增加或改变处理一个
    请求的那些职责。你可以将这种机制与静态的特例化处理对象的继承机制结合起来使用。

  • 3 不保证被接受:既然一个请求没有明确的接收者,那么就不能保证它一定会被处理 —
    该请求可能一直到链的末端都得不到处理。一个请求也可能因该链没有被正确配置而得不到
    处理。

8 实现

下面是在职责链模式中要考虑的实现问题:

  • 1 实现后继者链:有两种方法可以实现后继者链。
    • 1 定义新的链接(通常在Handler中定义,但也可由ConcreteHandler来定义);
    • 2 使用已有的链接
  • 2 连接后继者:如果没有已有的引用可定义一个链,那么你必须自己引入它们。这种情
    况下 Handler不仅定义该请求的接口,通常也维护后继链接。这样 Handler就提供了
    HandleRequest的缺省实现 : HandleRequest向后继者 (如果有的话 )转发请求。如果ConcreteHandler子类对该请求不感兴趣,它不需重定义转发操作,因为它的缺省实现进行无条件的转发;
  • 3 表示请求:可以有不同的方法表示请求。最简单的形式,比如在 HandleHelp的例子中,请求是一个硬编码的 (hard-coded) 操作调用。这种形式方便而且安全,但你只能转发 HandleHelp类定义的固定的一组请求。另一选择是使用一个处理函数,这个函数以一个请求码 (如一个整型常数或一个字符串 )为参数。这种方法支持请求数目不限。唯一的要求是发送方和接受方在请求如何编码问题上应达成一致。
    这种方法更为灵活,但它需要用条件语句来区分请求代码以分派请求。另外,无法用类
    型安全的方法来传递请求参数,因此它们必须被手工打包和解包。显然,相对于直接调用一
    个操作来说它不太安全。
    为解决参数传递问题,我们可使用独立的请求对象来封装请求参数。 Request类可明确地描述请求,而新类型的请求可用它的子类来定义。这些子类可定义不同的请求参数。处理者必须知道请求的类型(即它们正使用哪一个Request子类)以访问这些参数。
    为标识请求,Request可定义一个访问器( accessor)函数以返回该类的标识符。或者,如果实现语言支持的话,接受者可使用运行时的类型信息。
  • 4 在Smalltalk中自动转发。
9 代码示例

github地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容