责任链模式(职责链模式)详解

在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。

在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2的拦截器、JSP和Servlet等,所有这些,都可以考虑使用责任链模式来实现。

模式的定义与特点

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

注意:责任链模式也叫职责链模式。

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

责任链模式是一种对象行为型模式,其主要优点如下。

  1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

其主要缺点如下。

  1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

模式的结构与实现

通常情况下,可以通过数据链表来实现职责链模式的数据结构。

1. 模式的结构

职责链模式主要包含以下角色。

  1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。

其结构图如图 1 所示。客户端可按图 2 所示设置责任链。


图1 责任链模式的结构图


图2 责任链

2. 模式的实现

职责链模式的实现代码如下:

package chainOfResponsibility;
public class ChainOfResponsibilityPattern {   public static void main(String[] args) {  //组装责任链       Handler handler1 = new ConcreteHandler1();       Handler handler2 = new ConcreteHandler2();      handler1.setNext(handler2);    //提交请求 handler1.handleRequest("two");   }
}//抽象处理者角色
abstract class Handler { private Handler next;public void setNext(Handler next) { this.next = next;   }  public Handler getNext() {  return next;   }  //处理请求的方法public abstract void handleRequest(String request);
}//具体处理者角色1
class ConcreteHandler1 extends Handler {  public void handleRequest(String request) { if (request.equals("one")) {     System.out.println("具体处理者1负责处理该请求!");  } else {          if (getNext() != null) {       getNext().handleRequest(request);  } else { System.out.println("没有人处理该请求!");        }      }  }
}//具体处理者角色2
class ConcreteHandler2 extends Handler { public void handleRequest(String request) {   if (request.equals("two")) {          System.out.println("具体处理者2负责处理该请求!");      } else {       if (getNext() != null) {     getNext().handleRequest(request);   } else {        System.out.println("没有人处理该请求!");    }      }   }
}

程序运行结果如下:

具体处理者2负责处理该请求!

在上面代码中,我们把消息硬编码为 String 类型,而在真实业务中,消息是具备多样性的,可以是 int、String 或者自定义类型。因此,在上面代码的基础上,可以对消息类型进行抽象 Request,增强了消息的兼容性。

模式的应用实例

【例1】用责任链模式设计一个请假条审批模块。

分析:假如规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现。

首先,定义一个领导类(Leader),它是抽象处理者,包含了一个指向下一位领导的指针 next 和一个处理假条的抽象处理方法 handleRequest(int LeaveDays);然后,定义班主任类(ClassAdviser)、系主任类(DepartmentHead)和院长类(Dean),它们是抽象处理者的子类,是具体处理者,必须根据自己的权力去实现父类的 handleRequest(int LeaveDays) 方法,如果无权处理就将假条交给下一位具体处理者,直到最后;客户类负责创建处理链,并将假条交给链头的具体处理者(班主任)。图 3 所示是其结构图。


图3 请假条审批模块的结构图

程序代码如下:

package chainOfResponsibility;
public class LeaveApprovalTest {  public static void main(String[] args) {      //组装责任链      Leader teacher1 = new ClassAdviser();  Leader teacher2 = new DepartmentHead();    Leader teacher3 = new Dean();     //Leader teacher4=new DeanOfStudies();    teacher1.setNext(teacher2);      teacher2.setNext(teacher3);   //teacher3.setNext(teacher4);       //提交请求       teacher1.handleRequest(8);   }
}//抽象处理者:领导类
abstract class Leader {  private Leader next;  public void setNext(Leader next) {        this.next = next; }   public Leader getNext() {  return next;   }   //处理请求的方法   public abstract void handleRequest(int LeaveDays);
}//具体处理者1:班主任类
class ClassAdviser extends Leader {   public void handleRequest(int LeaveDays) {  if (LeaveDays <= 2) {        System.out.println("班主任批准您请假" + LeaveDays + "天。");     } else {        if (getNext() != null) {        getNext().handleRequest(LeaveDays);  } else {          System.out.println("请假天数太多,没有人批准该假条!");   }       }  }
}//具体处理者2:系主任类
class DepartmentHead extends Leader { public void handleRequest(int LeaveDays) {  if (LeaveDays <= 7) {  System.out.println("系主任批准您请假" + LeaveDays + "天。");  } else {      if (getNext() != null) {  getNext().handleRequest(LeaveDays);     } else {          System.out.println("请假天数太多,没有人批准该假条!");   }       }  }
}//具体处理者3:院长类
class Dean extends Leader {  public void handleRequest(int LeaveDays) {if (LeaveDays <= 10) {       System.out.println("院长批准您请假" + LeaveDays + "天。");  } else {        if (getNext() != null) {    getNext().handleRequest(LeaveDays);    } else {               System.out.println("请假天数太多,没有人批准该假条!");   }       }  }
}//具体处理者4:教务处长类
class DeanOfStudies extends Leader {    public void handleRequest(int LeaveDays) {    if (LeaveDays <= 20) {     System.out.println("教务处长批准您请假" + LeaveDays + "天。");   } else {    if (getNext() != null) {       getNext().handleRequest(LeaveDays);  } else {    System.out.println("请假天数太多,没有人批准该假条!"); }   }}
}

程序运行结果如下:

院长批准您请假8天。

假如增加一个教务处长类,可以批准学生请假 20 天,也非常简单,代码如下:

//具体处理者4:教务处长类
class DeanOfStudies extends Leader {  public void handleRequest(int LeaveDays) {    if (LeaveDays <= 20) {      System.out.println("教务处长批准您请假" + LeaveDays + "天。"); } else {       if (getNext() != null) {    getNext().handleRequest(LeaveDays);          } else {      System.out.println("请假天数太多,没有人批准该假条!");       }    }  }
}

模式的应用场景

前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用。

  1. 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
  2. 可动态指定一组对象处理请求,或添加新的处理者。
  3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

模式的扩展

职责链模式存在以下两种情况。

  1. 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
    场景

前边已经讲述了关于责任链模式的结构与特点,下面介绍其应用场景,责任链模式通常在以下几种情况使用。

  1. 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
  2. 可动态指定一组对象处理请求,或添加新的处理者。
  3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

模式的扩展

职责链模式存在以下两种情况。

  1. 纯的职责链模式:一个请求必须被某一个处理者对象所接收,且一个具体处理者对某个请求的处理只能采用以下两种行为之一:自己处理(承担责任);把责任推给下家处理。
  2. 不纯的职责链模式:允许出现某一个具体处理者对象在承担了请求的一部分责任后又将剩余的责任传给下家的情况,且一个请求可以最终不被任何接收端对象所接收。

责任链模式(职责链模式)详解——小马同学@Tian相关推荐

  1. 装饰模式(装饰设计模式)详解——小马同学@Tian

    装饰模式(装饰设计模式)详解 上班族大多都有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会,就会用方便的方式解决早餐问题.有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不 ...

  2. 《设计模式详解》行为型模式 - 职责链模式

    职责链模式 6.4 职责链模式 6.4.1 概述 6.4.2 结构 6.4.3 案例实现 6.4.4 优缺点 6.4.5 JavaWeb 源码 - FilterChain 完整的笔记目录:<设计 ...

  3. 责任链模式(职责链模式)(Chain of Responsibility Pattern)

    学校OA 系统的采购审批项目:需求是 采购员采购教学器材 如果金额小于等于5000, 由教学主任审批(0<=x<=5000) 如果金额小于等于10000, 由院长审批(5000<x& ...

  4. 15、设计模式-行为型模式-职责链模式

    职责链模式 很多情况下,在一个软件系统中可以处理某个请求的对象不止一个,例如SCM系统中的采购 单审批,主任.副董事长.董事长和董事会都可以处理采购单,他们可以构成一条处理采购 单的链式结构,采购单沿 ...

  5. 区块链用哪种语言 Java_区块链开发用什么语言呢?区块链与编程语言的关系详解...

    原标题:区块链开发用什么语言呢?区块链与编程语言的关系详解 区块链的概念就随着比特币的大热开始逐渐进入公众视野,比特币的拥有者为了使其创造更高的财富,开始疯狂炒作,因而引发购买比特币大浪潮.然而等到比 ...

  6. 我的世界服务器修改飞行速度,我的世界创造模式飞行速度修改教程详解

    这次百度攻略&搞趣网小编为诸位我的世界玩家带来的是我的世界创造模式飞行速度修改教程详解,希望诸位我的世界玩家会喜欢. 我的世界创造模式飞行速度修改教程详解 虽然飞行比走路要快很多,但是在建造大 ...

  7. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  8. 深入剖析Redis系列(三) - Redis集群模式搭建与原理详解

    前言 在 Redis 3.0 之前,使用 哨兵(sentinel)机制来监控各个节点之间的状态.Redis Cluster 是 Redis 的 分布式解决方案,在 3.0 版本正式推出,有效地解决了 ...

  9. 怎样进入android模式,安卓手机如何进入Recovery模式的通用方式详解

    2014-12-12 15:24:16 安卓手机如何进入Recovery模式的通用方式详解 标签:安卓 Recovery模式 教程 Recovery模式是什么?这里说的Recovery模式主要指的是安 ...

最新文章

  1. SQL关键字转换大写核心算法实现
  2. exec和source命令的区别
  3. 【转载】使用Nexus搭建Maven服务器详细配置
  4. 关于bufferKnife8.8.1点击事件无效的原因
  5. Git分支命令学习使用
  6. TVP两周年:携手同行,让未来可见
  7. 2014蓝桥杯-B-省赛-五-圆周率
  8. linux内核mtd驱动程序与sd卡驱动程序,Linux内核MTD驱动程序与SD卡驱动程序(2)
  9. csr 蓝牙驱动_双11来临之际,推荐几款无线蓝牙耳机吧
  10. 位置指纹(LF)定位技术简介-室内定位
  11. html2张图片垂直居中,任意图片实现垂直居中的三种方法(兼容性还不错)
  12. 从后台servlet中,获取jsp页面输入的值,来删除用户一行信息
  13. zend7 常用快捷键
  14. linux mysql 密码策略_Linux Ubuntu 14 MySQL 密码策略(复杂度)和审计插件
  15. 如何有效解决企业敏感文件泄露问题
  16. 大功率MOS管选型手册及可申请样品-KIA MOS管
  17. 计算机学院毕业生德育总结,毕业生德育答辩总结_相关文章专题_写写帮文库
  18. CV520国产兼容应用及说明
  19. oracle mysql 中文排序规则_Oracle 对汉字的order by排序规则
  20. java约等于符号_markdown常见数学符号和运算

热门文章

  1. 美国计算机科学专业申请条件,美国留学 计算机科学专业申请条件
  2. 不是秘密的内幕 常见移动电源质量问题
  3. SONY索尼MP4视频变RSV文件修复方法
  4. 目态 的目是什么意思
  5. Java AES 256加密解密示例
  6. webpack搭建vue脚手架
  7. Python3.6实现12306火车票自动抢票
  8. java程序填空题输出汤姆猫_教教你如何配置汤姆猫 和 Java 环境变量的设置
  9. python 小爱音箱集成_python爬取京东小爱音响评论
  10. jQuery里.prepend()和.prependTo的方法使用。