掌握设计模式--责任链模式
责任链模式(Chain of Responsibility)责任链模式(Chain of Responsibility)是一种行为型设计模式,旨在通过将请求沿着一系列处理者传递,形成一条处理链,直到请求被处理链上特定的结点处理为止。它允许多个对象有机会处理请求,而不需要明确指定哪个对象将处理该请求。每个处理者包含对下一个处理者的引用,如果当前处理者无法处理请求,它会将请求传递给下一个处理者。这样可以将请求的处理职责链式地分配给多个处理者,而不需要将它们紧密耦合。
组成部分
[*]抽象处理者(Handler):定义一个处理请求的接口,并且持有一个指向下一个处理者的引用。如果当前处理者无法处理请求,就将其传递给下一个处理者。
[*]具体处理者(ConcreteHandler):实现抽象处理者的接口,处理请求。如果无法处理,则传递给下一个处理者。
[*]客户端(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流写入文件,即可实现不同日志级别的信息写入到不同的日志文件。
优缺点和应用场景
优点
[*]降低耦合度:客户端不需要知道哪个具体的对象会处理请求,处理请求的对象可以动态变化;
[*]扩展性强:新的处理器可以很容易地被添加到责任链中,且不需要修改现有的代码;
[*]职责分离:每个处理者只关注自己能处理的逻辑,职责清晰。
缺点
[*]链过长时可能造成性能问题:请求可能在链中经过多个处理者,这可能导致性能上的损耗,尤其是责任链较长时;
[*]调试复杂性:由于请求被多个处理者处理,调试时可能较难追踪请求的流转路径;
[*]请求可能永远无法得到处理:如果责任链中的所有处理器都没有处理该请求,则请求会被忽略或终止。这种情况可能会导致某些请求得不到预期的处理结果,需要在设计时注意链的完整性和错误处理机制。
应用场景
[*]日志记录:责任链模式可以用于日志记录的处理。不同的日志级别(例如,INFO、DEBUG、ERROR)可以通过责任链模式传递,依次被不同的日志处理器(如控制台日志、文件日志、网络日志等)处理。
[*]权限校验:在复杂的权限校验中,不同的权限校验可以作为责任链的一部分,依次处理。每个处理器检查不同的权限要求,直到满足条件或结束。
例子:在SpringSecurity中,访问控制或权限校验可以通过过滤链模式来实现。例如,检查用户是否拥有访问某个页面的权限,可以通过多个权限处理器(如角色权限、用户权限、IP 白名单等)进行逐层处理。
[*]请求过滤:Servlet 的过滤器链(Filter Chain),每个过滤器负责请求的某个方面(例如,身份验证、权限检查、日志记录等)。请求被传递到链中的下一个过滤器,直到最终响应。
[*]表单验证:表单验证可以通过责任链模式进行处理,每个验证器可以处理不同的验证规则,直到表单满足所有验证条件。
例子:在表单提交时,可以有多个验证器(如空值检查、格式验证、长度验证、范围验证等),每个验证器都负责处理不同的验证逻辑。
[*]数据处理管道:在数据处理流程中,责任链模式适合于处理多个步骤的数据流。每个步骤可以视为一个处理器,负责对数据进行某种操作,数据会被传递到下一个步骤。
例子:数据清洗和转换流程中,每个数据清洗步骤(如去除空值、格式化、转换编码等)可以作为责任链的一部分,按顺序处理数据。
责任链模式的应用
Spring Security的过滤链(Filter Chain)是责任链模式的一种典型实现。它是一个按顺序执行的过滤器集合,负责拦截和处理HTTP请求,以实现认证、授权、安全控制等功能,并且支持自定义过滤器插入到Spring Security的过滤链中,从而实现自定义的安全处理逻辑,使得Spring Security变得更加灵活。
总结
责任链设计模式是一种行为设计模式,其核心在于将多个处理对象连接成一条链,允许请求沿链传递,直到有一个处理者能够处理该请求,从而实现请求的解耦和动态的处理顺序管理,并且处理者易于扩展,使得程序更加灵活。
需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。
<hr>超实用的SpringAOP实战之日志记录
2023年下半年软考考试重磅消息
通过软考后却领取不到实体证书?
计算机算法设计与分析(第5版)
Java全栈学习路线、学习资源和面试题一条龙
软考证书=职称证书?
软考中级--软件设计师毫无保留的备考分享
页:
[1]