跳至主要內容

【设计模式】行为型-④责任链模式

holic-x...大约 10 分钟设计模式设计模式

【设计模式】行为型-④责任链模式

学习核心

  • 责任链模式核心
    • 概念:责任链是一条链,链上的每个节点多有各自的责任,当前责任节点会确认自己可否处理输入,如果不能就交给下一个责任节点进行处理,以此类推直到最后一个责任节点
    • 构成:处理者(接口定义)、基础处理者(可选)、具体处理者
      • 处理者(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
模拟三级审核,审核通过
----------
模拟三级审核,审核通过
模拟二级审核,审核通过
----------
模拟三级审核,审核通过
模拟二级审核,审核通过
模拟一级审核,审核通过
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3