SpringBoot实践——责任链模式

4,320 阅读3分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

说明

定义

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

适用场景

  • 当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。
  • 如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。
  • 必须按顺序执行多个处理者时, 可以使用该模式。

优点

  • 你可以控制请求处理的顺序。
  • 单一职责原则。 你可对发起操作和执行操作的类进行解耦。
  • 开闭原则。 你可以在不更改现有代码的情况下在程序中新增处理者。

缺点

  • 部分请求未被处理
  • 链路太长性能受到影响

举例

  • SpringMvc 拦截器设计使用了责任链模式
  • Flowable工作流

代码实践

举例说明

举例实现一个登录过程中进行的各种校验,校验账号是否存在、密码是否正确、账号状态是否正常(仅仅只是举例)

代码

抽象处理类

public abstract  class AbstractLoginHandler<T> {
    /**
     * 责任链,下一个链接节点
     */
    protected AbstractLoginHandler<T> next = null;
    /**
     * 内部逻辑
     * @param loginDTO 登录DTO
     */
    public abstract String doHandler(LoginDTO loginDTO) throws Exception;

    public void next(AbstractLoginHandler handler) {
        this.next = handler;
    }
    public static class Builder<T> {
        private AbstractLoginHandler<T> head;
        private AbstractLoginHandler<T> tail;

        public Builder<T> addHandler(AbstractLoginHandler handler) {
            if (this.head == null) {
                this.head = handler;
                this.tail = handler;
            } else {
                this.tail.next(handler);
                this.tail = handler;
            }
            return this;
        }

        public AbstractLoginHandler build() {
            return this.head;
        }
    }
}

登录DTO

@Data
public class LoginDTO {
    String username; //用户
    String status;  //状态
    String password; //密码

}

具体逻辑处理:用户、密码、状态校验

/**
 * @author 勤任风
 * @date 2021-08-27 17:39
 * 用户是否存在
 */
public class UserExistHandler extends AbstractLoginHandler {

    @Override
    public String doHandler(LoginDTO loginDTO) throws Exception {
        String username = loginDTO.getUsername();
        //验证逻辑:如果用户不存在
        if(!"test_user".equals(username)){
            return "用户不存在";
        }
        // 判断是否还有下个责任链节点,没有的话,说明已经是最后一个节点
        if (next != null){
           return next.doHandler(loginDTO);
        }
        return "验证均已通过,登录成功";
    }
}
/**
 * @author 勤任风
 * @date 2021-08-27 17:44
 * 用户状态是否正常
 */
public class UserStatusHandler extends AbstractLoginHandler {
    @Override
    public String doHandler(LoginDTO loginDTO) throws Exception {
        String status = loginDTO.getStatus();
        //验证逻辑:如果用户已被冻结
        if("freeze".equals(status)){
            return "账户已被冻结";
        }
        // 判断是否还有下个责任链节点,没有的话,说明已经是最后一个节点
        if(next!=null){
            return next.doHandler(loginDTO);
        }
        return "验证均已通过,登录成功";
    }
}
/**
 * @author 勤任风
 * @date 2021-08-27 17:44
 * 验证密码
 */
public class UserPasswordHanler extends AbstractLoginHandler {
    @Override
    public String doHandler(LoginDTO loginDTO) throws Exception {
        String password = loginDTO.getPassword();
        //验证逻辑:用户密码
        if(!"123456".equals(password)){
            return "密码不正确";
        }
        // 判断是否还有下个责任链节点,没有的话,说明已经是最后一个节点
        if(next!=null){
            return next.doHandler(loginDTO);
        }
        return "验证均已通过,登录成功";
    }
}

测试Controller

@RestController
@RequestMapping("/responsibility")
public class ResponsibilityController {
    /**
     * 登录测试
     */
    @PostMapping("/login")
    public String login(@RequestBody LoginDTO loginDTO) throws Exception {
        AbstractLoginHandler.Builder<Object> builder = new AbstractLoginHandler.Builder<>();
        String result = builder.addHandler(new UserExistHandler())
                .addHandler(new UserStatusHandler())
                .addHandler(new UserPasswordHanler())
                .build().doHandler(loginDTO);
        return "登录结果:"+result;
    }
}

测试结果

image.png

image.png

image.png

image.png

参考

juejin.cn/post/698097…

refactoringguru.cn/design-patt…