【设计模式】行为型-④责任链模式
【设计模式】行为型-④责任链模式
注
学习核心
- 责任链模式核心
- 概念:责任链是一条链,链上的每个节点多有各自的责任,当前责任节点会确认自己可否处理输入,如果不能就交给下一个责任节点进行处理,以此类推直到最后一个责任节点
- 构成:处理者(接口定义)、基础处理者(可选)、具体处理者
- 处理者(Handler):接口定义(一些场景中会考虑直接将Handler定义为抽象类,提供一些通用的方法)
- 基础处理者(BaseHandler):这是一个可选的抽象类设计,可将一些公共的处理样本代码放至其中
- 具体处理者(ConcreteHandlers):实现接口,定义具体的请求处理逻辑(handler)和指向链中下一个处理者的引用(next)
- 客户端(Client):根据程序逻辑一次性或者动态生成链,请求可以发送给任意一个处理者,非必须是第一个处理者
- 应用场景分析
- 【BPM业务流程多级审批相关】:【上线流程审批】、【请假流程审批】等
- 【Spring框架】:Spring 框架的异常处理机制采用责任链模式
- 【JavaWeb开发】:过滤器、拦截器实现
- 【日志记录等级】:根据配置的日志等级,打印日志信息
学习资料
概念核心
责任链模式:责任链是一条链,链上的每个节点多有各自的责任,当前责任节点会确认自己可否处理输入,如果不能就交给下一个责任节点进行处理,以此类推直到最后一个责任节点。其核心是解决一组服务节点中的先后执行处理关系。
简单理解:最常见的场景就是BPM,流程类业务中一些审批概念,比如跳槽离职时要被安排得明明白白找各个leader签字
1.简单场景案例(请求链路处理)
此处设计涉及构成有:处理者(Handler,接口)、基础处理者(BaseHandler,抽象类)、具体处理者(ConcreteHandlers,具体类),实际一些业务场景中会将处理者直接定义为抽象类(类似将Handler接口定义和基础处理者功能合并),以此简化实现思路。例如此处可以简化为处理者(Handler,抽象类)、具体处理者(ConcreteHandlers,具体类)
- 处理者(Handler):接口定义
/**
* 请求处理器:定义处理接口
*/
public interface RequestHandler {
public void handle(String request);
}
- 基础处理者(BaseHandler):可选的抽象类定义(用于实现Handler,提供一些公共的实现和下一节点设定的方法)
/**
* 基础请求处理器:实现接口,定义公共的处理逻辑以及下一个节点的设定
*/
public abstract class BaseRequestHandler implements RequestHandler{
protected RequestHandler next;
// 设置下一个节点
public void setNext(RequestHandler next) {
this.next = next;
}
}
- 具体处理者(ConcreteHandlers):继承BaseHandler,实现业务逻辑和下一节点的跳转逻辑
/**
* 具体处理类:A
* 实现相应的业务逻辑并传递给下一个节点进行处理
*/
public class AHandler extends BaseRequestHandler{
@Override
public void handle(String request) {
// A 处理自己的业务逻辑
System.out.println("A 处理完成");
// 将请求转发给下一个节点(如果节点存在则执行handler逻辑)
if(this.next!=null){
this.next.handle(request);
}
}
}
// 类似地,B、C节点的设定也是基于此参考实现
- 客户端(Client):客户端实现指定链路关系,然后从指定节点启动
/**
* 责任链客户端测试
*/
public class HandlerClient {
public static void main(String[] args) {
// 1.创建对象节点(有哪些对象要执行任务的)
AHandler aHandler = new AHandler();
BHandler bHandler = new BHandler();
CHandler cHandler = new CHandler();
// 2.构建节点的责任链关系(此处设定为A->B->C)
aHandler.setNext(bHandler);
bHandler.setNext(cHandler);
// 3.执行业务逻辑(启动节点,链路可以从任一节点开始)
aHandler.handle("链路待处理数据"); // 从A节点开始
System.out.println("-------");
cHandler.handle("执行ING");// 从C节点开始
}
}
-- output
A 处理完成
B 处理完成
C 处理完成
-------
C 处理完成
2.日志记录器
涉及构成:抽象Handler(AbstractLogger日志记录器)、具体Handler(ConsoleLogger-info级别、FileLogger-debug级别、ErrorLogger-error级别)
- 抽象Handler
public abstract class AbstractLogger {
// 日志记录等级设定
protected int level;
// 定义下一个节点
protected AbstractLogger nextLogger;
// 定义处理方法
public void handler(int level, String message){
if(this.level <= level){
write(message);
}
// 如果下一节点存在则继续打印
if(nextLogger != null){
nextLogger.handler(level, message);
}
}
// 定义公共方法(下一节点设定)
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
// 定义抽象方法(打印日志)
public abstract void write(String msg);
}
- 具体Handler
/**
* 控制台打印处理
*/
public class ConsoleLogger extends AbstractLogger{
public ConsoleLogger(int level){
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("console log: " + msg);
}
}
/**
* File级别日志打印处理
*/
public class FileLogger extends AbstractLogger{
public FileLogger(int level){
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("file log: " + msg);
}
}
/**
* Error级别日志打印处理
*/
public class ErrorLogger extends AbstractLogger{
public ErrorLogger(int level){
this.level = level;
}
@Override
public void write(String msg) {
System.out.println("error log: " + msg);
}
}
- 客户端Client
/**
* 日志打印客户端测试
*/
public class LoggerClient {
// 1-INFO级别、2-DEBUG级别、3-ERROR级别
private static final int LEVEL_INFO = 1;
private static final int LEVEL_DEBUG = 2;
private static final int LEVEL_ERROR = 3;
private static AbstractLogger getLoggerChain(){
AbstractLogger consoleLogger = new ConsoleLogger(LEVEL_INFO);
AbstractLogger fileLogger = new FileLogger(LEVEL_DEBUG);
AbstractLogger errorLogger = new ErrorLogger(LEVEL_ERROR);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
// 返回责任链节点(最高级别的节点:头节点)
return errorLogger;
}
public static void main(String[] args) {
// 获取责任链
AbstractLogger loggerChain = getLoggerChain();
// 执行
loggerChain.handler(LEVEL_INFO,"this is an info level message");
System.out.println("----------");
loggerChain.handler(LEVEL_DEBUG,"this is an debug level message");
System.out.println("----------");
loggerChain.handler(LEVEL_ERROR,"this is an error level message");
}
}
-- output
console log: this is an info level message
----------
file log: this is an debug level message
console log: this is an debug level message
----------
error log: this is an error level message
file log: this is an error level message
console log: this is an error level message
场景案例分析
1.BPM流程:系统上线审批流程
【系统上线审批流程】节点分析:正常时间点上线只需要三级负责人审批即可,在一些特殊的业务时间点(例如618大促、双11、双12这些时间点),则需要相应更高级别的审批人加入审核节点。
🧨传统实现思路
此处的核心是需要构建审批链路,需要相应校验审核时间节点,如果在指定的时间阈值范围内,则需要进行相应的审核操作。参考代码实现如下
/**
* 传统实现方式:系统上线审批
* 根据上线时间节点,分级校验
*/
public class OnlineAuth {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 上线审核方法
* @param uid
* @param orderId
* @param authDate
*/
public static void doAuth(String uid, String orderId, Date authDate) throws Exception{
System.out.println("审批人:" + uid + " 审核单号:" + orderId + " 审批时间:" + sdf.format(authDate));
// 进行三级审批
System.out.println("模拟三级审批,审批通过");
// 判断是否需要二级审批(根据当前审批时间与二级审批节点阈值进行比较)
Date level2BeginDate = sdf.parse("2020-06-01 00:00:00");
Date level2EndDate = sdf.parse("2020-06-25 00:00:00");
if(authDate.after(level2BeginDate)&&authDate.before(level2EndDate)){
System.out.println("模拟二级审批,审批通过");
}
// 判断是否需要一级审批
Date level1BeginDate = sdf.parse("2020-06-11 00:00:00");
Date level1EndDate = sdf.parse("2020-06-20 00:00:00");
if(authDate.after(level1BeginDate)&&authDate.before(level1EndDate)){
System.out.println("模拟一级审批,审批通过");
}
}
}
Client 客户端测试
/**
* 系统上线流程审批客户端测试demo
*/
public class OnlineAuthClient {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws Exception {
// 测试1:日常上线
OnlineAuth.doAuth("1001","1",sdf.parse("2020-05-21 00:00:00"));
// 测试2:临近大促
OnlineAuth.doAuth("1002","2",sdf.parse("2020-06-03 00:00:00"));
// 测试3:大促期间
OnlineAuth.doAuth("1003","3",sdf.parse("2020-06-18 00:00:00"));
}
}
-- output
审批人:1001 审核单号:1 审批时间:2020-05-21 00:00:00
模拟三级审批,审批通过
审批人:1002 审核单号:2 审批时间:2020-06-03 00:00:00
模拟三级审批,审批通过
模拟二级审批,审批通过
审批人:1003 审核单号:3 审批时间:2020-06-18 00:00:00
模拟三级审批,审批通过
模拟二级审批,审批通过
模拟一级审批,审批通过
✨责任链模式实现思路
参考简单场景案例中的构建思路,此处将构成分为处理器(抽象类)、具体处理器(子类实现),通过客户端构建链路信息进行测试
- 审核信息实体类定义
/**
* 审核信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AuthInfo {
private String uid;
private String orderId;
private Date authDate;
}
- 处理器(抽象类)
public abstract class AbstractAuthHandler {
protected AbstractAuthHandler nextHandler;
// 设置下一节点
public void setNextHandler(AbstractAuthHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 定义处理方法
public abstract void handle(AuthInfo authInfo);
}
- 具体处理类(子类实现,分级处理)
处理的核心是校验当前审核时间是否在对应的时间阈值,进而决定审核的级别。
每个处理节点都需要校验当前是否为自己需要处理的节点,如果是则执行业务逻辑,随后判断是否存在下一个节点,将任务抛给下一个节点进行处理
public class Level3AuthHandler extends AbstractAuthHandler{
@Override
public void handle(AuthInfo authInfo) {
System.out.println("模拟三级审核,审核通过");
if(this.nextHandler!=null){
this.nextHandler.handle(authInfo);
}
}
}
public class Level2AuthHandler extends AbstractAuthHandler{
// 定义时间阈值
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void handle(AuthInfo authInfo) {
// 设定时间阈值
Date level2BeginDate = null;
Date level2EndDate = null;
try{
level2BeginDate = sdf.parse("2020-06-01 00:00:00");
level2EndDate = sdf.parse("2020-06-25 00:00:00");
}catch (Exception e){
e.printStackTrace();
}
// 校验时间阈值
Date authDate = authInfo.getAuthDate();
if(authDate.after(level2BeginDate) && authDate.before(level2EndDate)){
System.out.println("模拟二级审核,审核通过");
if(this.nextHandler!=null){
this.nextHandler.handle(authInfo);
}
}
}
}
public class Level1AuthHandler extends AbstractAuthHandler{
// 定义时间阈值
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void handle(AuthInfo authInfo) {
// 设定时间阈值
Date level1BeginDate = null;
Date level1EndDate = null;
try{
level1BeginDate = sdf.parse("2020-06-11 00:00:00");
level1EndDate = sdf.parse("2020-06-20 00:00:00");
}catch (Exception e){
e.printStackTrace();
}
// 校验时间阈值
Date authDate = authInfo.getAuthDate();
if(authDate.after(level1BeginDate) && authDate.before(level1EndDate)){
System.out.println("模拟一级审核,审核通过");
if(this.nextHandler!=null){
this.nextHandler.handle(authInfo);
}
}
}
}
- 客户端测试
public class AuthClient {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws Exception {
// 创建责任链节点
Level3AuthHandler level3AuthHandler = new Level3AuthHandler();
Level2AuthHandler level2AuthHandler = new Level2AuthHandler();
Level1AuthHandler level1AuthHandler = new Level1AuthHandler();
// 构建责任链关系
level3AuthHandler.setNextHandler(level2AuthHandler);
level2AuthHandler.setNextHandler(level1AuthHandler);
// 模拟测试
level3AuthHandler.handle(new AuthInfo("1001","1",sdf.parse("2020-05-21 00:00:00")));
System.out.println("----------");
level3AuthHandler.handle(new AuthInfo("1002","2",sdf.parse("2020-06-03 00:00:00")));
System.out.println("----------");
level3AuthHandler.handle(new AuthInfo("1003","3",sdf.parse("2020-06-18 00:00:00")));
}
}
-- output
模拟三级审核,审核通过
----------
模拟三级审核,审核通过
模拟二级审核,审核通过
----------
模拟三级审核,审核通过
模拟二级审核,审核通过
模拟一级审核,审核通过