本文的目录地址

本文的代码地址

开发一个应用时,多数时候我们都能预先知道哪个方法能处理某个特定请求。然而,情况并非总是如此。例如,在广播计算机网络中,会将请求发送给所有节点,但仅对所发送内容感兴趣的节点会处理请求。

如果一个节点对某个请求不感兴趣或者不知道该如何处理这个请求,可以执行以下两个操作。

  • 忽略这个请求,什么都不做
  • 将请求转发给下一个节点

节点对一个请求的反应方式是实现的细节。然而,我们可以使用广播计算机网络来类比理解责任链模式是什么。责任链(Chain of Responsibility)模式用于让多个对象来处理单个请求时,或者用于预先不知道应该由哪个对象(来自某个对象链)来处理某个特定请求时。其原则如下

  • 存在一个对象链(链表/树或任何其他便捷的数据结构)。
  • 我们一开始将请求发送给链中的第一个对象。
  • 对象决定其是否要处理该请求。
  • 对象将请求转发给下一个对象。
  • 重复该过程,直到到达链尾。

软件的例子

Java的servlet过滤器是在一个HTTP请求到达目标处理程序之前执行的一些代码片段。在使用servlet过滤器时,有一个过滤器链,其中每个过滤器执行一个不同动作(用户身份验证、记日志、数据压缩等),并且将请求转发给下一个过滤器知道链结束;如果发生错误(例如,连续三次身份验证失败)则跳出处理流程。

Apple的Cocoa和Cocoa Touch框架使用责任链来处理事件。在某个视图接收到一个其并不知道如何处理的事件时,会将事件转发给其超视图,直到有个视图能够处理这个事件或者视图链结束。

应用案例

通过使用责任链模式,我们能让许多不同对象来处理一个特定请求。在我们预先不知道应该由哪个对象来处理请求时,这是有用的。其中一个例子是采购系统。在采购系统中,有许多核准权限。某个核准权限可以核准在一定额度之内的订单,假设为100美元。如果订单超过了100美元,则会将订单发送给链中的下一个核准权限,比如能够核准在200美元以下的订单,等等。

另一个责任链例子,单个事件,比如一次鼠标点击,可被多个事件监听者捕获。

不过应该注意,如果所有请求都能被单个处理程序处理,责任链就没用了,除非确实不知道会是哪个程序处理请求。这一模式的价值在于解耦。客户端与所有处理程序之间不再是多对多的关系,客户端仅需要知道如何与链的起始节点进行通信。

实现

这里将会实现一个简单的事件系统。下面是该系统的UML类图。

Event类描述一个事件。为了让它简单一点,在我们的案例中一个事件只有一个name属性。

class Event:def __init__(self,name):self.name=namedef __str__(self):return self.name

Widget类是应用的核心类。UML图中展示的parent聚合关系表明每个控件都有一个到父对象的引用。按照约定,我们假设父对象是一个Widget实例。

class Widget:def __init__(self,parent=None):self.parent=parent

handle()方法使用动态分发,通过hasattr()和getattr()决定一个特定请求(event)应该由谁来处理。如果被请求处理事件的控件并不支持该事件,则有两种回退机制。如果控件有parent,则执行parent的handle()方法。如果控件没有parent,但有handle_default()方法,则执行handle_default()方法。

    def handle(self,event):handler='handle_{}'.format(event)if hasattr(self,handler):method=getattr(self,handler)method(event)elif self.parent:self.parent.handle(event)elif hasattr(self,'handle_default'):self.handle_default(event)

此时,你可能已明白为什么UML类图中Widget和Event类仅是关联关系而已(不是聚合或组合关系)。关联关系用于表明Widget类知道Event类,但对其没有任何严格的引用,因为事件仅需要作为参数传递给handle()。

MainWindow、MsgText和SendDialog是具有不同行为的控件。我们并不期望这三个控件都能处理相同的事件,即使它们能处理相同事件,表现出来也可能是不同的。MainWindow仅能处理close和default事件。

class MainWidow(Widget):def handle_close(self,event):print('MainWindow: {}'.format(event))def handle_default(self,event):print('MainWindow: {}'.format(event))

SendDialog仅能处理paint事件。

class SendDialog(Widget):def handle_paint(self,event):print('SendDialog: {}'.format(event))

最后,MsgText只能处理down事件。

class MsgText(Widget):def handle_down(self, event):print('MsgText: {}'.format(event))

main()函数展示如何创建一些控件和事件,以及控件如何对那些事件做出反应。所有事件都会被发送给所有控件。注意其中每个控件的父子关系。sd对象(SendDialog的一个实例)的父对象是mw(MainWindow的一个实例)。然而,并不是所有对象都需要一个MainWindow实例的父对象。例如,msg对象是以sd作为父对象。

def main():mw=MainWidow()sd=SendDialog(mw)msg=MsgText(sd)for e in ('down','paint','unhandled','close'):evt=Event(e)print('\nSending event -{}- to MainWindow'.format(evt))mw.handle(evt)print('Sending event -{}- to SendDialog'.format(evt))sd.handle(evt)print('Sending event -{}- to MsgText'.format(evt))msg.handle(evt)

完整代码文件chain.py,运行结果如下

Sending event -down- to MainWindow
MainWindow: down
Sending event -down- to SendDialog
MainWindow: down
Sending event -down- to MsgText
MsgText: downSending event -paint- to MainWindow
MainWindow: paint
Sending event -paint- to SendDialog
SendDialog: paint
Sending event -paint- to MsgText
SendDialog: paintSending event -unhandled- to MainWindow
MainWindow: unhandled
Sending event -unhandled- to SendDialog
MainWindow: unhandled
Sending event -unhandled- to MsgText
MainWindow: unhandledSending event -close- to MainWindow
MainWindow: close
Sending event -close- to SendDialog
MainWindow: close
Sending event -close- to MsgText
MainWindow: close

从输出中我们能看到一些有趣的东西。例如,发送一个down事件给MainWindow,最终被MainWindow默认处理函数处理。另一个不错的用例是,虽然close事件不能被SendDialog和MsgText直接处理,但所有close事件最终都能被MainWindow正确处理。这正是使用父子关系作为一种回退机制的优美之处。

python 实现 责任链模式相关推荐

  1. python责任链模式

    责任链模式是一种设计模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求.发出这个请求的客户端并不知道链上的哪一个对象最 ...

  2. 设计模式之五 责任链模式(Chain of Responsibility)

    2019独角兽企业重金招聘Python工程师标准>>> 一. 场景 相信我们都有过这样的经历: 我们去职能部门办理一个事情,先去了A部门,到了地方被告知这件事情由B部门处理: 当我们 ...

  3. Python设计模式-职责链模式

    Python设计模式-职责链模式 代码基于3.5.2,代码如下; #coding:utf-8 #职责链模式class Handler():def __init__(self):self.success ...

  4. 最近学习了责任链模式

    2019独角兽企业重金招聘Python工程师标准>>> 前言 来菜鸟这个大家庭10个月了,总得来说比较融入了环境,同时在忙碌的工作中也深感技术积累不够,在优秀的人身边工作必须更加花时 ...

  5. 《JAVA与模式》之责任链模式

    2019独角兽企业重金招聘Python工程师标准>>> 详细请访问原博客:http://www.cnblogs.com/java-my-life/archive/2012/05/28 ...

  6. 责任链模式 和观察者模式

    2019独角兽企业重金招聘Python工程师标准>>> 责任链模式平常没怎么用过,今天看到了责任链模式的基本构成.大体就是把一件事情分给n个互相有引用的类去执行.比如解析ip地址.过 ...

  7. 设计模式--责任链模式

    2019独角兽企业重金招聘Python工程师标准>>> 责任链模式(chain of responsibility): 有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链 ...

  8. 轻松学习Java设计模式之责任链模式

    我们的态度是:每天进步一点点,理想终会被实现. 前言 设计模式,可能很多人都是看到代码知道怎么回事,但是离开代码再让其说出来,估计就有点含糊其词了,包括我自己在内.Android中其实用到的设计模式也 ...

  9. Spring中如何使用责任链模式

    2019独角兽企业重金招聘Python工程师标准>>> 关于责任链模式,其有两种形式,一种是通过外部调用的方式对链的各个节点调用进行控制,从而进行链的各个节点之间的切换:另一种是链的 ...

  10. Chain of Responsibility 责任链模式 MD

    责任链模式 简介 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上[传递],直到链上的某一个对象决定处理此请求.发出这个请求的客户 ...

最新文章

  1. 在Ubuntu 14.04 64bit上编译安装xbt tracker
  2. 【Android 安装包优化】资源混淆 ( resources.arsc 资源映射表文件格式 | 头文件 数据格式 | 全局字符串池 数据格式 | 包数据 数据格式 | 包头 数据格式 )
  3. angular routerlink传递参数_[翻译]在 Angular 中使用 async-await 特性
  4. chrome浏览器被reimage pair 劫持怎么处理
  5. php函数get和set,php中外部类调用_get函数和_set函数的方法
  6. 数据结构之交换排序:快速排序
  7. 流程企业(钢铁企业)的制造执行系统
  8. hdu 2141 Can you find it(二分)
  9. oracle错误输出,oracle – SQL小提琴输出错误
  10. Java事件处理机制的两个案例
  11. SpingMVC 注解@RequestMapping、@SuppressWarnings、@Scheduled 定时器
  12. 如何把.bat文件设置为开机自动启动?
  13. Onedrive不限速还有5T空间,且行且珍惜
  14. Accept CS Ph.D. Offer from Stony Brook University,去SUNY石溪大学的CS Ph.D.啦
  15. 小程序FMP优化实录,已拿offer附真题解析
  16. Oracle GoldenGate Monitor agent安装后的报错OGGMON-20603
  17. 【转载】Python3.6安装报错 configure: error: no acceptable C compiler found in $PATH
  18. VMWare虚拟机安装WIN10系统【21H1长效稳定专业版】【图文详细教程】
  19. C语言计算级数fun,c语言编程 编写函数fun(),它的功能是:计算和输出下列级数的和....
  20. echarts 饼状图

热门文章

  1. 微信小程序的点击、双击、长按事件
  2. vue 组件开发基本思路
  3. hello world!——VS使用教程
  4. https抓包与防抓包
  5. ps4插html屏幕不亮光,ps4连接显示器怎么老是黑屏
  6. eps、emf等图片格式转换
  7. 合成孔径雷达(微波遥感)的应用
  8. 最全常见算法工程师面试题目整理
  9. usb万能驱动win7_win10改win7教程
  10. 学 stm 32 单片机