4、责任链设计模式
大约 8 分钟
责任链设计模式
责任链模式定义
责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它将请求的发送者与接收者解耦,让多个接收者对象形成一条链,并沿着这条链传递请求,直到有一个接收者处理它为止。每个接收者都有权决定是否处理请求,或者将请求转发给链上的下一个接收者。
主要特点
- 请求传递:请求沿着链条依次传递,直到找到能够处理该请求的处理器。
- 灵活性:可以动态增加或删除处理器,只需要通过继承或者实现的方式实现一个具体处理者并且改变引用关系即可,调整处理链的顺序。
- 解耦:请求发送者和处理者之间没有直接的依赖关系。
使用场景
- 请求需要多个对象进行处理:例如,在订单处理系统中,不同的订单状态可能由不同的处理器进行处理,一个成交单由多种状态,不同的状态交给不同的处理器执行;
- 动态的请求处理链:如审批流程,处理链可以根据业务需求进行动态配置。
- 处理请求可能有多种方式或多种条件,需要在运行时动态确定。
- 不希望请求发送者与接收者之间存在紧密耦合。
- 需要对请求进行过滤、分发、路由等操作。
责任链模式类图
组件角色说明:
Handler(处理者)接口/抽象类: 定义处理请求的接口,通常包含一个处理请求的方法(如 handleRequest()
),以及一个设置或获取下一个处理者的方法(如 setNext()
和 getNext()
)。
ConcreteHandler(具体处理者): 实现处理者接口,负责处理特定类型的请求。每个具体处理者在处理请求时,可以选择处理请求、拒绝处理(通常通过返回特定值或抛出异常表示)或将其转发给下一个处理者。
使用原理:
请求发起者只需将请求发送给链上的第一个处理者,后续的传递过程完全由链上的处理者自行决定。每个处理者检查请求是否符合其处理条件,若符合则处理并终止传递,否则将请求转发给下一个处理者。这样,请求的发送者无需关心请求如何被处理,处理的责任分散在链上的各个处理者中。
日志打印案例
定义info级别的日志无法打印debug级别的日志,debug级别的日志无法打印error级别的日志,因此在打印业务日志时,不同级别的日志使用不同的打印处理器进行打印处理,下面我们使用责任链实现此功能;
定义日志处理器抽象类
public abstract class AbstractLogger {
/*责任链中的日志等级 规定info不能打印debug日志 debug日志不能打印error日志 但是反过来可以*/
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
/*定义当前日志界别*/
protected int level;
//责任链中的下一个元素
protected AbstractLogger nextLogger;
/**
* 日志打印逻辑
* @author yourname
* @date 15:09 2024/12/23
* @param level
* @param message
**/
public void logMessage(int level,String message){
/*如果当前对象可以处理请求,就直接处理请求*/
if(this.level >= level){
write(message);
}else if(nextLogger !=null){
/*如果自己处理不了,并且下一个链不为空,就传递给下一个*/
nextLogger.logMessage(level, message);
}else {
/*如果都处理不了,就打印错误*/
System.out.println("Logger ERROR :exit Progreming:" + message);
}
}
/**
* 设置责任链的下一个节点
* @author yourname
* @date 14:55 2024/12/23
* @param nextLogger
**/
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
/**
* 子类写日志方法,具体由子类实现
* @author yourname
* @date 14:12 2024/12/23
* @param message
**/
abstract protected void write(String message);
}
AbstractLogger:定义责任链的下一个节点,如果当前节点无法处理,就交给下一个节点处理;
此抽象类由具体类实现,子类实现write具体打印方法,在本例中,实现error,info,debug级别的打印;
Info级别打印
public class InfoConsoleLogger extends AbstractLogger{
public InfoConsoleLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("INFO Standard Console::Logger: " + message);
}
}
Debug级别打印
public class DebugConsoleLogger extends AbstractLogger{
public DebugConsoleLogger(int level){
this.level = level;
}
/**
* 重写父类write方法,写日志到控制台
* @author yourname
* @date 14:13 2024/12/23
* @param message
**/
@Override
protected void write(String message) {
System.out.println("DEBUG Standard Console::Logger: " + message);
}
}
Error级别打印
public class ErrorConsoleLogger extends AbstractLogger{
public ErrorConsoleLogger(int level){
this.level = level;
}
/**
* 重写父类write方法,写日志到控制台
* @author yourname
* @date 14:13 2024/12/23
* @param message
**/
@Override
protected void write(String message) {
System.out.println("ERROR Standard Console::Logger: " + message);
}
}
Client测试
public class Chain_test_API {
/**
* 创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
* @author yourname
* @date 14:22 2024/12/23
* @return bugcode.online.designPattern.Chain.AbstractLogger
**/
public static AbstractLogger getChainOfLogger(){
/*定义责任链 info-debug-error 实现不同级别的日志由不同的日志打印处理器处理*/
AbstractLogger errorLogger = new ErrorConsoleLogger(AbstractLogger.ERROR);
AbstractLogger infoLogger = new InfoConsoleLogger(AbstractLogger.INFO);
AbstractLogger debugLogger = new DebugConsoleLogger(AbstractLogger.DEBUG);
/*设置责任链下一个链*/
infoLogger.setNextLogger(debugLogger);
debugLogger.setNextLogger(errorLogger);
return infoLogger;
}
public static void main(String[] args) {
AbstractLogger chainOfLogger = getChainOfLogger();
/*打印debug级别日志*/
chainOfLogger.logMessage(AbstractLogger.DEBUG,"DEBUG:this is a debug log");
/*打印info级别日志*/
chainOfLogger.logMessage(AbstractLogger.INFO,"INFO:this is a info log");
}
}
//打印结果
DEBUG Standard Console::Logger: DEBUG:this is a debug log
INFO Standard Console::Logger: INFO:this is a info log
请求处理案例
创建抽象处理器
public abstract class Handler {
public static final String TYPEA = "TYPEA";
public static final String TYPEB = "TYPEB";
/*保存责任链下一个节点的引用*/
protected Handler nextHandler;
/*设置下一个节点*/
public void setNext(Handler handler) {
this.nextHandler = handler;
}
/*子类实现的请求处理类*/
public abstract void handleRequest(Request request);
}
创建处理器A
public class ConcreteHandlerA extends Handler{
@Override
public void handleRequest(Request request) {
if (canHandle(request)) {
// 当前处理器可以处理请求
System.out.println("ConcreteHandlerA handled the request.");
} else if (nextHandler != null) {
/*将当前请求转发给下一个节点处理v*/
nextHandler.handleRequest(request);
} else {
/*没有处理器可以处理请求*/
System.out.println("No suitable handler found for the request.");
}
}
/*判断当前处理器是狗可以处理请求*/
private boolean canHandle(Request request) {
// 根据请求内容判断处理者A是否能处理该请求
// 这里仅作为示例,实际逻辑应根据业务需求实现
return request.getType().equals(Handler.TYPEA) && request.getPriority() >= 5;
}
}
创建处理器B
public class ConcreteHandlerB extends Handler{
@Override
public void handleRequest(Request request) {
if (canHandle(request)) {
// 处理请求...
System.out.println("ConcreteHandlerB handled the request.");
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("No suitable handler found for the request.");
}
}
private boolean canHandle(Request request) {
// 根据请求内容判断处理者B是否能处理该请求
// 这里仅作为示例,实际逻辑应根据业务需求实现
return request.getType().equals(Handler.TYPEB) && request.getPriority() <= 8;
}
}
客户端构件责任链
public class Chain_Handle_client_test_API {
public static void main(String[] args) {
/*创建具体处理器*/
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
/*设置处理器之间的责任链*/
handlerA.setNext(handlerB);
Request requestA = new Request("TYPEA", 7); // 创建请求对象A
Request requestB = new Request("TYPEB", 6); // 创建请求对象B
// handlerA.handleRequest(requestA); // 发起请求A
handlerA.handleRequest(requestB); // 发起请求B
}
}
//结果
ConcreteHandlerB handled the request.
优缺点
优点
- 发送者与接收者解耦,增加系统的灵活性,易于扩展新的处理者或调整处理顺序。
- 便于实现请求的分发、过滤、路由等功能。
- 通过在链上动态地添加或删除处理者,可以改变系统的处理行为。
缺点
- 链条过长可能导致请求处理的效率下降。
- 如果处理链的构造不合理,可能会导致请求得不到处理(例如,链中没有能够处理该请求的处理者)。
- 调试较为困难,因为请求的处理过程可能涉及多个处理者。