0.参考资料



1.概述

  • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。 —《设计模式》GoF
    • 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
    • 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推
  • 职责链模式(Chain Of Responsibility),使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
  • 核心: 单一职责+开闭原则

1.1动机

  1. - 在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。
  2. - 如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。

1.2结构

  1. - ![[IW@OW4LS%@}PT)DUI6IUKQ.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628769759573-28ea391f-7c1f-46d3-99d1-2be337aeed7c.png#clientId=u4ee7efec-c79c-4&from=paste&height=262&id=u3f3d8052&margin=%5Bobject%20Object%5D&name=%5BIW%40OW4LS%25%40%7DPT%29DUI6IUKQ.png&originHeight=523&originWidth=1054&originalType=binary&ratio=1&size=113564&status=done&style=none&taskId=u1fc71651-6ee5-44df-a52e-2234cb0836f&width=527)

2.要点总结

宏架构观上

  1. 1. Chain of Responsibility模式的应用场合在于: 一个请求可能有多个接受者,但是最后真正的接受者只有一个, 这时候请求发送者与接受者的遇合有可能出现“变化脆弱”的症状,职责链的目的就是二者解耦, 从而更好地应对变化。
  2. 1. 应用了Chain of Responsibility模式后,对象的职责分派将更具灵活性。我们可以在运行时动态添加修改请求的处理职责。
  3. 1. 如果请求传递到职责链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任.

微观代码上

  1. 1. 将请求和处理分开,实现解耦,提高系统的灵活性
  2. 1. 简化了对象,使对象不需要知道链的结构
  3. 1. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在Handler中设置一个最大节点数量,在setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  4. 1. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  5. 1. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java WebTomcatEncoding的处理、拦截器

3.案例

3.1需求:OA系统采购审批

  1. - 学校OA系统的采购审批项目:需求是
  2. - 采购员采购教学器材
  3. - 如果金额 小于等于5000, 由教学主任审批 0<=x<=5000
  4. - 如果金额 小于等于10000, 由院长审批 (5000<x<=10000)
  5. - 如果金额 小于等于30000, 由副校长审批 (10000<x<=30000)
  6. - 如果金额 超过30000以上,有校长审批 ( 30000<x)

3.2传统方式解决

  1. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628774907510-181f724b-db1c-4df7-8d62-03344ee3d627.png#clientId=u5874d48b-5f6e-4&from=paste&height=170&id=uc0b30b27&margin=%5Bobject%20Object%5D&name=image.png&originHeight=339&originWidth=317&originalType=binary&ratio=1&size=18617&status=done&style=none&taskId=u70b16aaa-5f13-453c-b307-0f3e62fe5dd&width=158.5)

3.3分析

  1. - 传统方案解决OA系统审批问题分析
  2. - 传统方式是:接收到一个采购请求后,根据采购金额来调用对应的Approver (审批人)完成审批
  3. - **传统方式的问题分析** : 客户端这里会使用到 分支判断(比如 switch) 来对不同的采购请求处理, 这样就存在如下问题
  4. - (1) 如果各个级别的人员审批金额发生变化,在客户端的也需要变化
  5. - (2) 客户端必须明确的知道 有多少个审批级别和访问
  6. - 这样 对一个采购请求进行处理 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护
  7. - 解决方案 => 职责链模式

4.使用模式

方案

  • 8.职责链模式(Chain of Resposibility)-行为型 - 图1

    类图


  • image.png
  • 说明

    • Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时包含另外的一个Handler (即后继者)
    • ConcreteHandlerA/B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
    • Request , 表示一个请求

      代码

    • 请求类 ```java @Data @AllArgsConstructor @NoArgsConstructor public class PurchaseRequest {

    // 描述信息 String requestName;

    // 请求金额 int price; }

  1. - 抽象处理类及其实现子类
  2. ```java
  3. // 父抽象处理类
  4. @Data
  5. @NoArgsConstructor
  6. @AllArgsConstructor
  7. public abstract class AbstHandler {
  8. public String handlerName;
  9. public AbstHandler nextHandler;
  10. public AbstHandler(String handlerName){
  11. this.handlerName = handlerName;
  12. }
  13. public abstract void processRequest(PurchaseRequest request);
  14. }
  15. // 以下是具体子类...........
  16. public class L1Handler extends AbstHandler{
  17. @Override
  18. public void processRequest(PurchaseRequest request) {
  19. if (request.getPrice() <= 5000){
  20. System.out.println(this.handlerName + "审批成功, 请求名: " + request.getRequestName() + ", 请求金额: " + request.getPrice());
  21. } else {
  22. nextHandler.processRequest(request);
  23. }
  24. }
  25. }
  26. public class L2Handler extends AbstHandler{
  27. @Override
  28. public void processRequest(PurchaseRequest request) {
  29. if (request.getPrice() <= 10000 && request.getPrice() > 5000){
  30. System.out.println(this.handlerName + "审批成功, 请求名: " + request.getRequestName() + ", 请求金额: " + request.getPrice());
  31. } else {
  32. nextHandler.processRequest(request);
  33. }
  34. }
  35. }
  36. public class L3Handler extends AbstHandler{
  37. @Override
  38. public void processRequest(PurchaseRequest request) {
  39. if (request.getPrice() <= 30000 && request.getPrice() > 10000){
  40. System.out.println(this.handlerName + "审批成功, 请求名: " + request.getRequestName() + ", 请求金额: " + request.getPrice());
  41. } else {
  42. nextHandler.processRequest(request);
  43. }
  44. }
  45. }
  46. public class L4Handler extends AbstHandler{
  47. @Override
  48. public void processRequest(PurchaseRequest request) {
  49. if (request.getPrice() > 30000){
  50. System.out.println(this.handlerName + "审批成功, 请求名: " + request.getRequestName() + ", 请求金额: " + request.getPrice());
  51. } else {
  52. nextHandler.processRequest(request);
  53. }
  54. }
  55. }
  1. - 客户端
  1. public class Client {
  2. public static void main(String[] args) {
  3. L1Handler l1Handler = new L1Handler();
  4. L2Handler l2Handler = new L2Handler();
  5. L3Handler l3Handler = new L3Handler();
  6. L4Handler l4Handler = new L4Handler();
  7. l1Handler.setHandlerName("张三主任");
  8. l2Handler.setHandlerName("李四院长");
  9. l3Handler.setHandlerName("王五副校长");
  10. l4Handler.setHandlerName("赵六校长");
  11. l1Handler.setNextHandler(l2Handler);
  12. l2Handler.setNextHandler(l3Handler);
  13. l3Handler.setNextHandler(l4Handler);
  14. // 职责链设置成环形
  15. l4Handler.setNextHandler(l1Handler);
  16. // 模拟接受组
  17. AbstHandler[] handlerArray = new AbstHandler[] {l1Handler, l2Handler, l3Handler, l4Handler};
  18. // 模拟抽象请求
  19. PurchaseRequest request = new PurchaseRequest("测试请求编号", 0);
  20. // 模拟处理具体请求: 随机金额 + 随机初始接受人
  21. for (int i = 0, count = 0; i <= 50000; i+=3000){
  22. //模拟递增金额
  23. request.setRequestName("第" + count + "号");
  24. request.setPrice(i);
  25. // 模拟随机初始处理人
  26. int index = (int) (Math.random() * 4);
  27. AbstHandler abstHandler = handlerArray[index];
  28. abstHandler.processRequest(request);
  29. }
  30. }
  31. }
  1. - 测试结果
  1. 张三主任审批成功, 请求名: 0号, 请求金额: 0
  2. 张三主任审批成功, 请求名: 0号, 请求金额: 3000
  3. 李四院长审批成功, 请求名: 0号, 请求金额: 6000
  4. 李四院长审批成功, 请求名: 0号, 请求金额: 9000
  5. 王五副校长审批成功, 请求名: 0号, 请求金额: 12000
  6. 王五副校长审批成功, 请求名: 0号, 请求金额: 15000
  7. 王五副校长审批成功, 请求名: 0号, 请求金额: 18000
  8. 王五副校长审批成功, 请求名: 0号, 请求金额: 21000
  9. 王五副校长审批成功, 请求名: 0号, 请求金额: 24000
  10. 王五副校长审批成功, 请求名: 0号, 请求金额: 27000
  11. 王五副校长审批成功, 请求名: 0号, 请求金额: 30000
  12. 赵六校长审批成功, 请求名: 0号, 请求金额: 33000
  13. 赵六校长审批成功, 请求名: 0号, 请求金额: 36000
  14. 赵六校长审批成功, 请求名: 0号, 请求金额: 39000
  15. 赵六校长审批成功, 请求名: 0号, 请求金额: 42000
  16. 赵六校长审批成功, 请求名: 0号, 请求金额: 45000
  17. 赵六校长审批成功, 请求名: 0号, 请求金额: 48000

5.经典使用


5.1SpringMVC中

5.1.1职责链模式在SpringMVC框架应用的源码分析

  1. - SpringMVC-HandlerExecutionChain 类就使用到职责链模式
  2. - ![BLY5T]8{UFM3$JQ_75UZ)@X.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628777640204-ad413db9-39e1-4dc1-92bf-2b8899098150.png#clientId=u5874d48b-5f6e-4&from=paste&height=296&id=ue330d137&margin=%5Bobject%20Object%5D&name=BLY5T%5D8%7BUFM3%24JQ_75UZ%29%40X.png&originHeight=591&originWidth=1484&originalType=binary&ratio=1&size=296973&status=done&style=none&taskId=u38ab720a-b1a0-420e-b521-3dd43d87297&width=742)
  3. - ![YX4[KSK7L5RMUXSW[_5D%~N.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1628778913719-52e5d194-dbb4-416c-8185-2fcd25377379.png#clientId=ub9605a92-fe78-4&from=paste&height=194&id=u95ee8925&margin=%5Bobject%20Object%5D&name=YX4%5BKSK7L5RMUXSW%5B_5D%25~N.png&originHeight=388&originWidth=1257&originalType=binary&ratio=1&size=613355&status=done&style=none&taskId=uc7aa0b38-dd11-4546-ae1d-ab48b470200&width=628.5)
  4. - 说明
  5. - springMVC 请求的流程图中,执行了 拦截器相关方interceptor.preHandler 等等
  6. - HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
  7. - HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器
  8. - 在处理SpringMvc请求时,使用到职责链模式还使用到 :[9.适配器模式()-XX型](https://www.yuque.com/ryze_java/design_pattern/stu8be?view=doc_embed).

5.2Tomcat中

5.2.1FilterChain

5.2.2JavaWeb中Tomcat对Encoding的处理