溯源设备 发表于 2025-2-6 18:11:48

「全网最细 + 实战源码案例」设计模式——责任链模式

核心思想


[*]责任链模式(CoR Pattern)是一种行为型设计模式,允许你将请求沿着处理者链进行发送,收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。
https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/85d0e365a5e84990a616c7752bfcba51~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1738922663&x-orig-sign=UOKWXfIDann7YSFwD%2FDkF5%2BdJJU%3D
<hr>结构

1. Handler(抽象处理者)


[*]定义了所有具体处理者的通用接口,通常包含用于处理请求的单个方法和设置链路上下一个处理者的方法。
2. ConcreteHandler(具体处理者)


[*]实现 Handler 接口,处理请求的具体业务逻辑和将请求传递给下一个处理者。
3. Client(客户端)


[*]创建责任链并发起请求,请求可发送给链上任意一个处理者,并非一定是第一个。
https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/42946752ef84473fb4c6d405f005d3f2~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1738922663&x-orig-sign=qBxkUPxw%2F0reZOTXRTTmywumxlU%3D
<hr>适用场景

1. 需要不同方式处理不同种类请求,且请求类型和顺序预先未知:


[*]该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。
2. 必须按顺序执行多个处理者:


[*]无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。
3. 所需处理者及其顺序必须在运行时改变:


[*]如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。
4. 应用场景:


[*]日志处理(不同级别日志由不同的 Logger 处理)
[*]Java Web 过滤器(javax.servlet.Filter )
[*]权限认证(用户权限按层级处理)
[*]责任审批流(如 OA 系统审批流程)
[*]中间件设计(请求的拦截、解析、转发等)
<hr>优点:

1. 遵循单一职责原则


[*]对请求者和接收者解耦。
2. 遵循开闭原则


[*]在不修改客户端代码的前提下创建新的处理者。
3. 可以控制请求处理的顺序


[*]动态调整链的顺序,甚至在运行时修改。
<hr>缺点:

1. 请求可能不会被处理


[*]没有合适的处理者时,该请求可能会被丢弃。
<hr>实现步骤

1. 声明处理者接口并描述请求处理方法的签名。


[*]确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。
2. 为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。


[*]该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。
[*]为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为。
3. 依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:


[*]是否自行处理这个请求。
[*]是否将该请求沿着链进行传递。
4. 客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。

5. 客户端可以触发链中的任意处理者, 而不仅仅是第一个。 请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾。

6. 由于链的动态性, 客户端需要准备好处理以下情况:


[*]链中可能只有单个链接。
[*]部分请求可能无法到达链尾。
[*]其他请求可能直到链尾都未被处理。
<hr>示例

https://p0-xtjj-private.juejin.cn/tos-cn-i-73owjymdk6/556111cb57644609b1131a61aeb97a9c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgU2xhY2tDbGltYg==:q75.awebp?policy=eyJ2bSI6MywidWlkIjoiODY5MDc1Mjk1NDEwMDA5In0%3D&rk3s=e9ecf3d6&x-orig-authkey=f32326d3454f2ac7e96d3d06cdbb035152127018&x-orig-expires=1738922663&x-orig-sign=xDrqsgWwaW9B2uKWqXw8GmPa8U0%3D
1. 经典实现——Handler

// 请假条public class LeaveRequest {    private String name;    private int num;    private String content;    public LeaveRequest(String name, int num, String content) {      this.name = name;      this.num = num;      this.content = content;    }    public String getName() {      return name;    }    public int getNum() {      return num;    }    public String getContent() {      return content;    }}// 抽象处理者——Handlerpublic abstract class Handler {    protected final static int NUM_ONE = 1;    protected final static int NUM_THREE = 3;    protected final static int NUM_SEVEN = 7;    private int numStart;    private int numEnd;    @Override    public String toString() {      return "Handler{" +                "numStart=" + numStart +                ", numEnd=" + numEnd +                ", nextHandler=" + nextHandler +                '}';    }    protected Handler nextHandler;    public Handler(int numStart, int numEnd) {      this.numStart = numStart;      this.numEnd = numEnd;    }    public Handler(int numStart){      this.numStart = numStart;    }    // 设置下一个处理者——上级领导    public void setNextHandler(Handler nextHandler) {      this.nextHandler = nextHandler;    }    // 当前处理者处理请假请求    protected abstract void handleLeave(LeaveRequest leave);    // 提交请假条    public final void submit(LeaveRequest leave) {      // 判断请假条的请假天数是否在当前处理者能够处理的范围内      if (leave.getNum() >= this.numStart && leave.getNum() <= this.numEnd) {            // 在,当前处理者处理请假条            this.handleLeave(leave);      } else if (this.nextHandler != null){            // 不在,提交给下一个处理者——上级领导            this.nextHandler.submit(leave);      } else {            // 已经没有下一个处理者了,无法处理            System.out.println("无法处理");      }    }}// 具体处理者——组长public class GroupLeader extends Handler{    public GroupLeader() {      super(0, Handler.NUM_ONE);    }    @Override    public void handleLeave(LeaveRequest leave) {                System.out.println("组长已审批:" + leave.getName() + "同学的请假条,请假天数为" + leave.getNum() + "天,请假原因为:" + leave.getContent());    }}// 具体处理者——经理public class Manager extends Handler{    public Manager() {      super(Handler.NUM_ONE, Handler.NUM_THREE);    }    @Override    public void handleLeave(LeaveRequest leave) {      System.out.println("经理已审批:" + leave.getName() + "同学的请假条,请假天数为:" + leave.getNum() + "天,请假原因为:" + leave.getContent());    }}// 具体处理者——总经理public class GeneralManager extends Handler{    public GeneralManager() {      super(Handler.NUM_THREE, Handler.NUM_SEVEN);    }    @Override    public void handleLeave(LeaveRequest leave) {      System.out.println("总经理已审批:" + leave.getName() + "同学的请假条,请假天数为:" + leave.getNum() + "天,请假原因为:" + leave.getContent());    }}// 测试类public class Client {    public static void main(String[] args) {      // 请假条      LeaveRequest leave = new LeaveRequest("小明", 5, "事假");      // 创建所有处理者      Handler groupLeader = new GroupLeader();      Handler manager = new Manager();      Handler generalManager = new GeneralManager();      // 组装处理者成链      groupLeader.setNextHandler(manager);      manager.setNextHandler(generalManager);      System.out.println(groupLeader);      System.out.println(manager);      System.out.println(generalManager);      // 调用处理者的submit方法,提交请假条      groupLeader.submit(leave);      System.out.println("--------------------------------------");      leave = new LeaveRequest("小红", 1, "事假");      groupLeader.submit(leave);      System.out.println("--------------------------------------");      leave = new LeaveRequest("小张", 2, "事假");      groupLeader.submit(leave);    }}2. 优化实现——Filter

// 请假条public class LeaveRequest {    private String name;    private int num;    private String content;    public LeaveRequest(String name, int num, String content) {      this.name = name;      this.num = num;      this.content = content;    }    public String getName() {      return name;    }    public int getNum() {      return num;    }    public String getContent() {      return content;    }}// 过滤器链public class FilterChain {    // 过滤器集合,执行顺序    private List<Filter> filters = new ArrayList<>();    // 过滤器索引——表示当前执行到filters集合中的第几个过滤器    private int index = 0;    public void setIndex(int index) {      this.index = index;    }    // 添加过滤器    public void addFilter(Filter filter) {      filters.add(filter);    }    // 执行所有过滤器    public void doFilter(LeaveRequest request) {      if (index == filters.size()) {            return;      }      Filter filter = filters.get(index ++);      filter.doFilter(request, this);    }}// 抽象拦截器——Filterpublic interface Filter {    void doFilter(LeaveRequest request, FilterChain chain);}// 具体过滤器——组长public class GroupLeader implements Filter {    @Override    public void doFilter(LeaveRequest request, FilterChain chain) {      if (request.getNum() > 0 && request.getNum() <= 1){            System.out.println("组长已审批:" + request.getName() + "同学的请假条," +                  "请假天数为 " + request.getNum() + "天,原因:" + request.getContent());      }else {            chain.doFilter(request);      }    }}// 具体拦截器——经理public class Manager implements Filter {    @Override    public void doFilter(LeaveRequest request, FilterChain chain) {      if (request.getNum() > 1 && request.getNum() <= 3){            System.out.println("经理已审批:" + request.getName() + "同学的请假条," +                  "请假天数为 " + request.getNum() + "天,原因:" + request.getContent());      }else {            chain.doFilter(request);      }    }}// 具体过滤器——总经理public class GeneralManager implements Filter {    @Override    public void doFilter(LeaveRequest request, FilterChain chain) {      if (request.getNum() > 3 && request.getNum() <= 7){            System.out.println("总经理已审批:" + request.getName() + "同学的请假条," +                  "请假天数为 " + request.getNum() + "天,原因:" + request.getContent());      }else {            System.out.println("请假天数过长,总经理不批准!");      }    }}// 测试类public class Client {    public static void main(String[] args) {      // 创建一个请假条对象      LeaveRequest leave = new LeaveRequest("小李", 5, "结婚");      // 创建一个过滤器链      FilterChain chain = new FilterChain();      chain.addFilter(new GroupLeader());      chain.addFilter(new Manager());      chain.addFilter(new GeneralManager());      // 提交请求      chain.doFilter(leave);      System.out.println("--------------------------------------");      chain.setIndex(0);      leave = new LeaveRequest("小张", 2, "事假");      chain.doFilter(leave);      System.out.println("--------------------------------------");      chain.setIndex(0);      leave = new LeaveRequest("小红", 1, "事假");      chain.doFilter(leave);    }}3. 优化点

优化点传统 Handler 方式Filter 方式责任链的维护需要 setNextHandler()明确指定下一个处理者通过 List动态管理责任链动态扩展性责任链是硬编码的,添加/删除处理器麻烦可以随时 addFilter()动态修改顺序控制必须按 setNextHandler()的顺序执行责任链顺序完全可配置短路能力处理器必须自己检查 nextHandler直接 return终止责任链<hr>在源码中的应用

Servlet 中的 Filter 采用了责任链模式来实现请求和响应的预处理和后处理

// 1️⃣ 过滤器接口(Servlet API 定义)interface Filter {    void doFilter(Request request, Response response, FilterChain chain);}// 2️⃣ 过滤器链(Servlet API 定义)interface FilterChain {    void doFilter(Request request, Response response);}// 3️⃣ 过滤器链的具体实现(Tomcat `ApplicationFilterChain`)class ApplicationFilterChain implements FilterChain {    private List<Filter> filters = new ArrayList<>(); // 过滤器列表    private int pos = 0;// 记录当前执行到哪个过滤器    private Servlet servlet; // 最终的目标 Servlet    public ApplicationFilterChain(Servlet servlet) {      this.servlet = servlet;    }    public void addFilter(Filter filter) {      filters.add(filter);    }    @Override    public void doFilter(Request request, Response response) {      if (pos < filters.size()) {            filters.get(pos++).doFilter(request, response, this);      } else {            servlet.service(request, response); // 所有 Filter 执行完后,进入 Servlet      }    }}// 4️⃣ 具体过滤器示例class AuthFilter implements Filter {    @Override    public void doFilter(Request request, Response response, FilterChain chain) {      System.out.println("【AuthFilter】权限验证...");      chain.doFilter(request, response); // 继续调用下一个 Filter 或 Servlet    }}class LogFilter implements Filter {    @Override    public void doFilter(Request request, Response response, FilterChain chain) {      System.out.println("【LogFilter】记录日志...");      chain.doFilter(request, response);    }}// 5️⃣ 目标 Servletclass MyServlet implements Servlet {    public void service(Request request, Response response) {      System.out.println("【Servlet】处理请求...");    }}// 6️⃣ 客户端模拟 Servlet 容器调用public class ServletFilterChainDemo {    public static void main(String[] args) {      // 创建 Servlet      Servlet servlet = new MyServlet();      // 创建过滤器链      ApplicationFilterChain filterChain = new ApplicationFilterChain(servlet);      filterChain.addFilter(new AuthFilter()); // 权限过滤器      filterChain.addFilter(new LogFilter());// 日志过滤器      // 模拟请求      Request request = new Request();      Response response = new Response();      // 执行过滤器链      filterChain.doFilter(request, response);    }}<hr>与其他模式的关系


[*]责任链模式、 命令模式、 中介者模式和观察者模式用于处理请求发送者和接收者之间的不同连接方式:


[*]

[*]责 任 链按照顺序将请求动态传递给一系列的潜在接收者, 直至其中一名接收者对请求进行处理。
[*]命 令在发送者和请求者之间建立单向连接。
[*]中 介 者清除了发送者和请求者之间的直接连接, 强制它们通过一个中介对象进行间接沟通。
[*]观 察 者允许接收者动态地订阅或取消接收请求。



[*]责任链通常和组合模式结合使用。 在这种情况下, 叶组件接收到请求后, 可以将请求沿包含全体父组件的链一直传递至对象树的底部。
[*]责任链的管理者可使用命令模式实现。 在这种情况下, 你可以对由请求代表的同一个上下文对象执行许多不同的操作。
[*]还有另外一种实现方式, 那就是请求自身就是一个命令对象。 在这种情况下, 你可以对由一系列不同上下文连接而成的链执行相同的操作。
[*]责任链和装饰模式的类结构非常相似。 两者都依赖递归组合将需要执行的操作传递给一系列对象。 但是, 两者有几点重要的不同之处。
[*]责任链的管理者可以相互独立地执行一切操作, 还可以随时停止传递请求。 另一方面, 各种装饰可以在遵循基本接口的情况下扩展对象的行为。 此外, 装饰无法中断请求的传递。
页: [1]
查看完整版本: 「全网最细 + 实战源码案例」设计模式——责任链模式