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

责任链模式(Chain of Responsibility)

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

image

组成部分

  1. 抽象处理者(Handler):定义一个处理请求的接口,并且持有一个指向下一个处理者的引用。如果当前处理者无法处理请求,就将其传递给下一个处理者。
  2. 具体处理者(ConcreteHandler):实现抽象处理者的接口,处理请求。如果无法处理,则传递给下一个处理者。
  3. 客户端(Client):发起请求,并将请求发送到责任链的起始点。

案例实现

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

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

设计思路

  • handleLog 方法:每个 LogHandler 只关注自己的日志级别,并在处理完成后调用 nextHandlerhandleLog 方法;
  • 责任链的链式处理LoggerChain 类负责维护日志处理器的顺序,并且通过一个 nextHandler 参数将责任传递给下一个处理器,实现责任的传递;
  • 灵活扩展:不需要每个处理器显式管理 nextHandler,在理想情况下只需要维护日志枚举类即可。

案例类图

image

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 信息.");
    }
}

执行结果

image

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

优缺点和应用场景

优点

  1. 降低耦合度:客户端不需要知道哪个具体的对象会处理请求,处理请求的对象可以动态变化;
  2. 扩展性强:新的处理器可以很容易地被添加到责任链中,且不需要修改现有的代码;
  3. 职责分离:每个处理者只关注自己能处理的逻辑,职责清晰。

缺点

  1. 链过长时可能造成性能问题:请求可能在链中经过多个处理者,这可能导致性能上的损耗,尤其是责任链较长时;
  2. 调试复杂性:由于请求被多个处理者处理,调试时可能较难追踪请求的流转路径;
  3. 请求可能永远无法得到处理:如果责任链中的所有处理器都没有处理该请求,则请求会被忽略或终止。这种情况可能会导致某些请求得不到预期的处理结果,需要在设计时注意链的完整性和错误处理机制。

应用场景

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

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

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

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

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

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

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

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

责任链模式的应用

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

总结

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

image

需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。


超实用的SpringAOP实战之日志记录

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

软考中级--软件设计师毫无保留的备考分享

posted @   渊渟岳  阅读(460)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek-R1本地部署如何选择适合你的版本?看这里
· 开源的 DeepSeek-R1「GitHub 热点速览」
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 揭秘 Sdcb Chats 如何解析 DeepSeek-R1 思维链
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
点击右上角即可分享
微信分享提示