跳转到正文
宋柏成
返回

责任链模式

责任链模式

什么是责任链模式

责任链模式(Chain of Responsibility Pattern)它允许你构建一个对象链,每个对象都持有对下一个对象的引用,从而形成一条链。每个对象在收到请求后,可以选择处理请求或将请求传递给链中的下一个对象。 这种模式的核心思想是解耦发送者和接收者,让多个对象都有机会处理请求,而不需要显式指定接收者。请求会沿着链传递,直到有一个对象处理它为止。

角色

优点

缺点

场景

总之符合多层处理流程的功能都可以用责任链模式来应对。

应用场景

用责任链实现请求内容校验

用户注册的场景下,我们在创建新用户之前需要经过对字段合法性、用户是否已注册、用户黑名单校验等步骤,如果只是将步骤分为不同的方法或者都写在同一个方法里难免会造成一个大类,维护起来十分困难,这里使用责任链的模式进行拆分。

  1. 抽象处理者 这里我们抽象处理者需要有处理方法和分类标识,处理方法当然是我们责任链每个环节的的具体实现,而分类标识则用来分类处理者种类从而实现多种处理者并存。这里我们还继承 Spring 的 Ordered 来实现每种责任链中处理者的执行排序。
import org.springframework.core.Ordered;

/**
 * @description: 抽象业务责任链
 **/
public interface AbstractChainHandler<T> extends Ordered {

    /**
     * 执行责任链逻辑
     *
     * @param requestParam 责任链执行入参
     */
    void handler(T requestParam);

    /**
     * 责任链组件标识
     */
    String mark();
}
  1. 具体处理者 这里我们分别实现校验注册字段的三个具体实现类,首先定于用户注册责任链的分类。
/**
 * @description: 用户责任链处理者
 **/
public interface UserRegisterCreateChainFilter <T extends UserRegisterReqVo> extends AbstractChainHandler<UserRegisterReqVo> {

    @Override
    default String mark() {
        return UserChainMarkEnum.USER_REGISTER_FILTER.name();
    }
}

有了注册责任链接口后,我们只需要继承这个接口实现具体的校验逻辑和顺序即可。

/**
 * @description: 用户注册参数必填检验
 **/
@Component
public class UserRegisterParamNotNullChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqVo> {

    @Override
    public void handler(UserRegisterReqVo requestParam) {
        if (Objects.isNull(requestParam.getUsername())) {
            throw new ClientException(UserRegisterErrorCodeEnum.USER_NAME_NOTNULL);
        } else if (Objects.isNull(requestParam.getPassword())) {
            throw new ClientException(UserRegisterErrorCodeEnum.PASSWORD_NOTNULL);
        } else if (Objects.isNull(requestParam.getTelephone())) {
            throw new ClientException(UserRegisterErrorCodeEnum.PHONE_NOTNULL);
        } else if (Objects.isNull(requestParam.getIdType())) {
            throw new ClientException(UserRegisterErrorCodeEnum.ID_TYPE_NOTNULL);
        } else if (Objects.isNull(requestParam.getIdCard())) {
            throw new ClientException(UserRegisterErrorCodeEnum.ID_CARD_NOTNULL);
        } else if (Objects.isNull(requestParam.getMail())) {
            throw new ClientException(UserRegisterErrorCodeEnum.MAIL_NOTNULL);
        } else if (Objects.isNull(requestParam.getRealName())) {
            throw new ClientException(UserRegisterErrorCodeEnum.REAL_NAME_NOTNULL);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

/**
 * @description: 用户注册用户名唯一检验
 **/
@Component
@RequiredArgsConstructor
public class UserRegisterHasUsernameChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqVo> {

    private final UserLoginService userLoginService;

    @Override
    public void handler(UserRegisterReqVo requestParam) {
        if (userLoginService.hasUsername(requestParam.getUsername())) {
            throw new ClientException(UserRegisterErrorCodeEnum.USERNAME_REGISTERED);
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

/**
 * @description: 用户注册检查证件号是否多次注销
 **/
@Component
@RequiredArgsConstructor
public class UserRegisterCheckDeletionChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqVo> {

    private final UserInfoService userInfoService;

    @Override
    public void handler(UserRegisterReqVo requestParam) {
        Integer userDeletionNum = userInfoService.queryUserDeletionNum(requestParam.getIdType(), requestParam.getIdCard());
        if (userDeletionNum >= 5) {
            throw new ClientException("证件号多次注销账号已被加入黑名单");
        }
    }

    @Override
    public int getOrder() {
        return 2;
    }
}
  1. 创建责任链 我们使用 Spring 的容器夹在组件的方式创建责任链,我们使用 Spring 提供的 CommandLineRunner 接口实现项目启动时获取所有继承我们抽象处理者的类并且按照 mark 分类和 Ordered 顺序来进行排序。
/**
 * @param <T> 请求参数的类型
 * @description: 抽象责任链上下文
 */
public class AbstractChainContext<T> implements CommandLineRunner {

    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

    @Override
    public void run(String... args) {
        // 获取所有具体执行者组件
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class);

        // 根据责任链组件标识将组件分类
        chainFilterMap.values().forEach(bean -> {
            abstractChainHandlerContainer
                    .computeIfAbsent(bean.mark(), k -> new ArrayList<>())
                    .add(bean);
        });

        // 按照组件 order 优先级进行排序
        abstractChainHandlerContainer.replaceAll((mark, handlers) ->
                handlers.stream()
                        .sorted(Comparator.comparing(Ordered::getOrder))
                        .collect(Collectors.toList())
        );
    }

    /**
     * 责任链组件执行
     *
     * @param mark         责任链组件标识
     * @param requestParam 请求参数
     */
    public void handler(String mark, T requestParam) {
        List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
        if (CollectionUtils.isEmpty(abstractChainHandlers)) {
            throw new RuntimeException(String.format("[%s] 责任链标识未定义。", mark));
        }
        abstractChainHandlers.forEach(each -> each.handler(requestParam));
    }
}
  1. 使用责任链
@Transactional(rollbackFor = Exception.class)
@Override
public UserRegisterRespVo register(UserRegisterReqVo requestParam) {
    // 登录责任链
    abstractChainContext.handler(UserChainMarkEnum.USER_REGISTER_FILTER.name(), requestParam);
    // 其他逻辑省略
   
}

分享这篇文章:

上一篇
DDD 领域驱动设计
下一篇
Java 开发规范