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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。

核心思想

  • 责任链模式(CoR Pattern)是一种行为型设计模式,允许你将请求沿着处理者链进行发送,收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。


结构

1. Handler(抽象处理者)

  • 定义了所有具体处理者的通用接口,通常包含用于处理请求的单个方法和设置链路上下一个处理者的方法。

2. ConcreteHandler(具体处理者)

  • 实现 Handler 接口,处理请求的具体业务逻辑和将请求传递给下一个处理者。

3. Client(客户端)

  • 创建责任链并发起请求,请求可发送给链上任意一个处理者,并非一定是第一个。


适用场景

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

  • 该模式能将多个处理者连接成一条链。 接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。 这样所有处理者都有机会来处理请求。

2. 必须按顺序执行多个处理者:

  • 无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。

3. 所需处理者及其顺序必须在运行时改变:

  • 如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。

4. 应用场景:

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

优点:

1. 遵循单一职责原则

  • 对请求者和接收者解耦。

2. 遵循开闭原则

  • 在不修改客户端代码的前提下创建新的处理者。

3. 可以控制请求处理的顺序

  • 动态调整链的顺序,甚至在运行时修改。

缺点:

1. 请求可能不会被处理

  • 没有合适的处理者时,该请求可能会被丢弃。

实现步骤

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

  • 确定客户端如何将请求数据传递给方法。 最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。

2. 为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。

  • 该类需要有一个成员变量来存储指向链上下个处理者的引用。 你可以将其设置为不可变类。 但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。
  • 为了使用方便, 你还可以实现处理方法的默认行为。 如果还有剩余对象, 该方法会将请求传递给下个对象。 具体处理者还能够通过调用父对象的方法来使用这一行为。

3. 依次创建具体处理者子类并实现其处理方法。 每个处理者在接收到请求后都必须做出两个决定:

  • 是否自行处理这个请求
  • 是否将该请求沿着链进行传递

4. 客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。 在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。

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

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

  • 链中可能只有单个链接
  • 部分请求可能无法到达链尾
  • 其他请求可能直到链尾都未被处理。

示例

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;
    }
}
// 抽象处理者——Handler
public 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);
    }
}
// 抽象拦截器——Filter
public 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

终止责任链


在源码中的应用

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️⃣ 目标 Servlet
class 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);
    }
}

与其他模式的关系

  • 按照顺序将请求动态传递给一系列的潜在接收者 直至其中一名接收者对请求进行处理
  • 在发送者和请求者之间建立单向连接
  • 清除了发送者和请求者之间的直接连接 强制它们通过一个中介对象进行间接沟通
  • 允许接收者动态地订阅或取消接收请求
  • 责任链通常和组合模式结合使用 在这种情况下 叶组件接收到请求后 可以将请求沿包含全体父组件的链一直传递至对象树的底部
  • 责任链的管理者可使用命令模式实现 在这种情况下 你可以对由请求代表的同一个上下文对象执行许多不同的操作
  • 还有另外一种实现方式, 那就是请求自身就是一个命令对象。 在这种情况下, 你可以对由一系列不同上下文连接而成的链执行相同的操作。
  • 责任链装饰模式的类结构非常相似 两者都依赖递归组合将需要执行的操作传递给一系列对象 但是 两者有几点重要的不同之处
  • 责任链的管理者可以相互独立地执行一切操作, 还可以随时停止传递请求。 另一方面, 各种装饰可以在遵循基本接口的情况下扩展对象的行为。 此外, 装饰无法中断请求的传递。
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
6天前
|
设计模式 存储 算法
「全网最细 + 实战源码案例」设计模式——命令模式
命令模式(Command Pattern)是一种行为型设计模式,将请求封装成独立对象,从而解耦请求方与接收方。其核心结构包括:Command(命令接口)、ConcreteCommand(具体命令)、Receiver(接收者)和Invoker(调用者)。通过这种方式,命令的执行、撤销、排队等操作更易扩展和灵活。 适用场景: 1. 参数化对象以操作。 2. 操作放入队列或远程执行。 3. 实现回滚功能。 4. 解耦调用者与接收者。 优点: - 遵循单一职责和开闭原则。 - 支持命令组合和延迟执行。 - 可实现撤销、恢复功能。 缺点: - 增加复杂性和类数量。
37 14
「全网最细 + 实战源码案例」设计模式——命令模式
|
8天前
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
46 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
8天前
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
51 7
「全网最细 + 实战源码案例」设计模式——模板方法模式
|
10天前
|
设计模式 存储 安全
「全网最细 + 实战源码案例」设计模式——组合模式
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它允许客户端以一致的方式对待单个对象和对象集合,简化了复杂结构的处理。组合模式包含三个主要组件:抽象组件(Component)、叶子节点(Leaf)和组合节点(Composite)。通过这种模式,客户端可以统一处理简单元素和复杂元素,而无需关心其内部结构。适用于需要实现树状对象结构或希望以相同方式处理简单和复杂元素的场景。优点包括支持树形结构、透明性和遵循开闭原则;缺点是可能引入不必要的复杂性和过度抽象。
65 22
|
2月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
131 11
|
3月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
20天前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
82 40
|
5月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
21天前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
52 19
|
19天前
|
设计模式 Java
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
49 12

热门文章

最新文章