写点什么

掌握设计模式 -- 责任链模式

作者:EquatorCoco
  • 2025-01-16
    福建
  • 本文字数:4147 字

    阅读完需:约 14 分钟

责任链模式(Chain of Responsibility)


责任链模式(Chain of Responsibility)是一种行为型设计模式,旨在通过将请求沿着一系列处理者传递,形成一条处理链,直到请求被处理链上特定的结点处理为止。它允许多个对象有机会处理请求,而不需要明确指定哪个对象将处理该请求。每个处理者包含对下一个处理者的引用,如果当前处理者无法处理请求,它会将请求传递给下一个处理者。这样可以将请求的处理职责链式地分配给多个处理者,而不需要将它们紧密耦合。



组成部分


  1. 抽象处理者(Handler):定义一个处理请求的接口,并且持有一个指向下一个处理者的引用。如果当前处理者无法处理请求,就将其传递给下一个处理者。

  2. 具体处理者(ConcreteHandler):实现抽象处理者的接口,处理请求。如果无法处理,则传递给下一个处理者。

  3. 客户端(Client):发起请求,并将请求发送到责任链的起始点。


案例实现


假设我们需要处理不同等级的日志信息,并根据不同的日志等级,将日志信息写入不同的日志文件。日志等级包括 info、debug、error 和 warning 日志级别。使用者只需指定日志级别,即可在责任链对象中自动处理对应的日志信息。


注:为了代码的实现简单,这里不编写具体的写入文件 IO 流操作,只是控制台输出。


设计思路


  • handleLog 方法:每个 LogHandler 只关注自己的日志级别,并在处理完成后调用 nextHandler 的 handleLog 方法;

  • 责任链的链式处理LoggerChain 类负责维护日志处理器的顺序,并且通过一个 nextHandler 参数将责任传递给下一个处理器,实现责任的传递;

  • 灵活扩展:不需要每个处理器显式管理 nextHandler,在理想情况下只需要维护日志枚举类即可。


案例类图



1. 定义 LogHandler 接口


在接口中,handleLog 方法接收一个 nextHandler 参数,决定是否将日志传递给下一个处理器。


public interface LogHandler {    void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler);}
复制代码


2. 具体日志处理器实现


每个日志处理器只关心自己负责的日志等级,如果当前处理器能处理,则输出日志,并将控制权交给下一个处理器。


public class InfoLogHandler implements LogHandler {    @Override    public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) {        if (logLevel.shouldLog(LoggerEnum.INFO)) {            System.out.println(this.getClass().getSimpleName() + ">> INFO: " + message);        }        // 如果存在下一个处理器        if (nextHandler != null) {            nextHandler.handleLog(logLevel, message, nextHandler);        }    }}
class DebugLogHandler implements LogHandler { @Override public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) { if (logLevel.shouldLog(LoggerEnum.DEBUG)) { System.out.println(this.getClass().getSimpleName() + ">> DEBUG: " + message); } // 如果存在下一个处理器 if (nextHandler != null) { nextHandler.handleLog(logLevel, message, nextHandler); } }}
class ErrorLogHandler implements LogHandler { @Override public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) { if (logLevel.shouldLog(LoggerEnum.ERROR)) { System.out.println(this.getClass().getSimpleName() + ">> ERROR: " + message); } // 如果存在下一个处理器 if (nextHandler != null) { nextHandler.handleLog(logLevel, message, nextHandler); } }}
class WarningLogHandler implements LogHandler { @Override public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) { if (logLevel.shouldLog(LoggerEnum.WARNING)) { System.out.println(this.getClass().getSimpleName() + ">> WARNING: " + message); } // 如果存在下一个处理器 if (nextHandler != null) { nextHandler.handleLog(logLevel, message, nextHandler); } }}
复制代码


3. 创建处理器链


public class LoggerChain implements LogHandler{    private int currentPosition = 0;    private List<LogHandler> handlers = new ArrayList<>();
// 初始化日志责任链时可以结合创建型设计模式来动态实现,符合开闭原则 public LoggerChain() { // 自动创建并排序处理器,按优先级从低到高 handlers.add(new InfoLogHandler()); handlers.add(new DebugLogHandler()); handlers.add(new ErrorLogHandler()); handlers.add(new WarningLogHandler()); }
// 处理日志 public void log(LoggerEnum logLevel, String message) { this.handleLog(logLevel,message,null); }
@Override public void handleLog(LoggerEnum logLevel, String message, LogHandler nextHandler) { if (currentPosition == handlers.size()) { // 退出责任链 currentPosition = 0; }else{ LogHandler firstHandler = handlers.get(currentPosition++); firstHandler.handleLog(logLevel, message, this); } }
}
复制代码


4. 日志等级枚举


LoggerEnum 枚举定义了不同的日志等级


public enum LoggerEnum {    INFO(1),    // 信息日志    DEBUG(2),   // 调试日志    ERROR(3),   // 错误日志    WARNING(4); // 警告日志
private final int priority;
LoggerEnum(int priority) { this.priority = priority; }
public int getPriority() { return priority; } // 判断当前等级是否符合输出要求:比如Debug级别的日志可以输出debug和info的日志 public boolean shouldLog(LoggerEnum currentLogLevel) { return this.priority >= currentLogLevel.getPriority(); }}
复制代码


5. 测试类


public class LoggerTest {    public static void main(String[] args) {        LoggerChain loggerChain = new LoggerChain();
// 模拟不同日志级别的请求 System.out.println("日志级别: INFO"); loggerChain.log(LoggerEnum.INFO, "这是 info 信息.");
System.out.println("\n日志级别: DEBUG"); loggerChain.log(LoggerEnum.DEBUG, "这是 debug 信息.");
System.out.println("\n日志级别: ERROR"); loggerChain.log(LoggerEnum.ERROR, "这是 error 信息.");
System.out.println("\n日志级别: WARNING"); loggerChain.log(LoggerEnum.WARNING, "这是 warning 信息."); }}
复制代码


执行结果



在这个案例中,初始化日志责任链时可以结合创建型设计模式来动态实现,才符合开闭原则,新增或删除日志级别时只需要维护枚举类即可。将控制台输出改为 IO 流写入文件,即可实现不同日志级别的信息写入到不同的日志文件。


优缺点和应用场景


优点


  1. 降低耦合度:客户端不需要知道哪个具体的对象会处理请求,处理请求的对象可以动态变化;

  2. 扩展性强:新的处理器可以很容易地被添加到责任链中,且不需要修改现有的代码;

  3. 职责分离:每个处理者只关注自己能处理的逻辑,职责清晰。


缺点


  1. 链过长时可能造成性能问题:请求可能在链中经过多个处理者,这可能导致性能上的损耗,尤其是责任链较长时;

  2. 调试复杂性:由于请求被多个处理者处理,调试时可能较难追踪请求的流转路径;

  3. 请求可能永远无法得到处理:如果责任链中的所有处理器都没有处理该请求,则请求会被忽略或终止。这种情况可能会导致某些请求得不到预期的处理结果,需要在设计时注意链的完整性和错误处理机制。


应用场景


1、日志记录:责任链模式可以用于日志记录的处理。不同的日志级别(例如,INFO、DEBUG、ERROR)可以通过责任链模式传递,依次被不同的日志处理器(如控制台日志、文件日志、网络日志等)处理。


2、权限校验:在复杂的权限校验中,不同的权限校验可以作为责任链的一部分,依次处理。每个处理器检查不同的权限要求,直到满足条件或结束。


例子:在 SpringSecurity 中,访问控制或权限校验可以通过过滤链模式来实现。例如,检查用户是否拥有访问某个页面的权限,可以通过多个权限处理器(如角色权限、用户权限、IP 白名单等)进行逐层处理。


3、请求过滤:Servlet 的过滤器链(Filter Chain),每个过滤器负责请求的某个方面(例如,身份验证、权限检查、日志记录等)。请求被传递到链中的下一个过滤器,直到最终响应。


4、表单验证:表单验证可以通过责任链模式进行处理,每个验证器可以处理不同的验证规则,直到表单满足所有验证条件。


例子:在表单提交时,可以有多个验证器(如空值检查、格式验证、长度验证、范围验证等),每个验证器都负责处理不同的验证逻辑。


5、数据处理管道:在数据处理流程中,责任链模式适合于处理多个步骤的数据流。每个步骤可以视为一个处理器,负责对数据进行某种操作,数据会被传递到下一个步骤。


例子:数据清洗和转换流程中,每个数据清洗步骤(如去除空值、格式化、转换编码等)可以作为责任链的一部分,按顺序处理数据。


责任链模式的应用


Spring Security 的过滤链(Filter Chain)是责任链模式的一种典型实现。它是一个按顺序执行的过滤器集合,负责拦截和处理 HTTP 请求,以实现认证、授权、安全控制等功能,并且支持自定义过滤器插入到 Spring Security 的过滤链中,从而实现自定义的安全处理逻辑,使得 Spring Security 变得更加灵活。


总结


责任链设计模式是一种行为设计模式,其核心在于将多个处理对象连接成一条链,允许请求沿链传递,直到有一个处理者能够处理该请求,从而实现请求的解耦和动态的处理顺序管理,并且处理者易于扩展,使得程序更加灵活。


文章转载自:渊渟岳

原文链接:https://www.cnblogs.com/dennyLee2025/p/18674080

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

EquatorCoco

关注

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
掌握设计模式--责任链模式_设计模式_EquatorCoco_InfoQ写作社区