首页 > 其他分享 >掌握设计模式--责任链模式

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

时间:2025-01-16 16:59:49浏览次数:3  
标签:LoggerEnum 请求 掌握 -- 处理 nextHandler 日志 设计模式 message

责任链模式(Chain of Responsibility)

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

image

组成部分

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

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

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

案例实现

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

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

设计思路

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

  • 责任链的链式处理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 白名单等)进行逐层处理。

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

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

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

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

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

责任链模式的应用

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

总结

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

文章转载自:渊渟岳

原文链接:掌握设计模式--责任链模式 - 渊渟岳 - 博客园

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

标签:LoggerEnum,请求,掌握,--,处理,nextHandler,日志,设计模式,message
From: https://blog.csdn.net/sdgfafg_25/article/details/145186228

相关文章

  • 渗透测试新手必刷14大靶场推荐
    目录标题基础靶场DVWApikachuxss-labssql-labsupload-labs极核CTF综合渗透靶场vulnstack红日靶场HackTheBox墨者学院TryHackMewebgoatvulfocusvulhubvulnhub基础靶场在学习渗透测试以及挖src的过程中,这些基础靶场必须得了解其原理以及利用其漏洞DVWA涵盖多种......
  • postgis镜像启动需要的属性
    postgis镜像启动需要的属性1.基础镜像PostGIS镜像通常基于官方的PostgreSQL镜像,你可以选择不同版本。例如:postgis/postgis是官方镜像。通常包含多个标记,例如postgis/postgis:15-3.3,表示PostgreSQL15和PostGIS3.3。2.环境变量可以通过环境变量配置PostGIS容......
  • JS节点操作
    目录1、创建节点2、创建文本3、添加节点4、替换节点5、删除节点6、克隆节点7、创建节点另外几种方式(1)、element.innerHTML(2)、element.innerText(3)、document.write()1、创建节点docment.createElement('节点')参数:标签名字符串这些元素原先不存在,是根据需求动......
  • 练习1
    以下将textarea作为输入框,run按钮添加了监听事件,pre作为输出框。test.html中的内容为:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=......
  • 全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之一维数组(应用二)
    实战训练1—统计相同的数问题描述:在一次实验中产生了n个数据,现在统计这n个数据中与指定数字m相同的个数,请编程实现。输入格式:输入共三行:第一行为一个整数n,表示整数序列的长度(n<=100);第二行为n个整数,整数之间以一个空格分开;第三行包含一个整数,表示指定的数字m。输出格式:输......
  • 高斯消元与高斯-约旦消元
    题目1洛谷P3389【模板】高斯消元法总的来说,就是求解一个nnn元一次方程组。高斯消元思路:首先把所有系数看成一个矩阵:......
  • Hyperparameter
    Hyperparameterhttps://blog.csdn.net/2401_85377976/article/details/141598610 Hyperparameter超参数(Hyperparameter),是机器学习算法中的调优参数,用于控制模型的学习过程和结构。与模型参数(ModelParameter)不同,模型参数是在训练过程中通过数据学习得到的,而超参数是在训练之......
  • 走进数据中心:了解定义、作用、分类,洞悉云计算大数据时代发展新趋势
    一、数据中心的定义、作用及分类数据中心是一整套复杂的设施,它不仅包括计算机系统和其他与之配套的网络、存储等设备,还包含冗余的数据通信连接设备、环境控制设备、监控设备以及各种安全装置。Google在其发布的《TheDatacenterasaComputer》一书中,将数据中心定义为:多功能......
  • stream流
    importjava.util.*;importjava.util.stream.Collectors;publicclassUserCollectionExample{publicstaticvoidmain(String[]args){//创建一个用户集合Listusers=Arrays.asList(newUser("张三",20,"北京"),newUser("李四",25,"......
  • yum和vim的使用
    目录一.Linux软件包管理器yum二.Linux编辑器vim使用1.vim的基本概念2.vim的基本操作3.vim正常模式命令4.vim末行模式命令5.vim的配置一.Linux软件包管理器yum什么是软件包        在Linux下安装软件,一个通常的办法是下载到程序的源代码,并进行编译,得......