设计模式-----责任链模式

74 阅读6分钟

责任链的两种处理方式

1、请求会被 所有的处理器都处理一遍,不存在中途终止的情况,这里参照 MyBatis 拦截器理解

image.png

2、二则是处理器链执行请求中,某一处理器执行时,如果不符合自制定规则的话,停止流程,并且剩下未执行处理器就不会被执行,大家参照 SpringMvc 拦截器理解

image.png

需要注意的问题

image.png

业务场景一:对某个功能设计一套校验规则(必填校验、非法字符校验、长度校验)

这里就不具体完成必填校验、非法字符校验、长度校验的规则了,以以下的校验规则为准,思想是类似的

非空校验、敏感词校验处理器

image.png

将两个处理器串成链

通过容器上下文获取指定类型的bean,添加到待处理集合中,由VerifyHandlerChain的verify方法处理 待处理集合,调用每个处理器的verify方法

image.png

image.png

业务场景二:对创建订单功能设计一套基础校验规则

image.png

接口设计

image.png

创建一个责任链上下文容器,用于存储与责任链相应的子任务。

public final class OrderCreateChainContext<T> implements CommandLineRunner {
    
    private final List<OrderCreateChainHandler> orderCreateChainHandlerContainer = new ArrayList();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(T requestParam) {
        // 此处根据 Ordered 实际值进行排序处理
        orderCreateChainHandlerContainer.stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
      	// 通过 Spring 上下文容器,获取所有 CreateOrderChainContext Bean
        Map<String, OrderCreateChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(OrderCreateChainHandler.class);
      	// 将对应 Bean 放入责任链上下文容器中
        chainFilterMap.forEach((beanName, bean) -> orderCreateChainHandlerContainer.add(bean););
    }
}

接口实现类

其中请求参数封装为一个OrderCreateCommand

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
	    // 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
	    // 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainHandler<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
	    // 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}

实际使用

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderCreateChainContext<OrderCreateCommand> orderCreateChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        orderCreateChainContext.handler(requestParam);
    }
}

责任链抽象(封装责任链模式组件)

当业务使用越来越多的情况下,重复定义 OrderCreateChainHandler 以及 OrderCreateChainContext 会增加系统冗余代码量。

可以考虑将这两个基础类抽象出来,作为基础组件库中的通用组件,供所有系统下的业务使用,从而避免代码冗余。

抽象基础类

抽象责任链处理接口

image.png

抽象责任链上下文

定义抽象责任链上下文,等同于 OrderCreateChainContext。可以看到保存责任链处理类的容器从 List 改为了 Map,这样可以方便扩展更多的不同业务责任链子类。

public final class AbstractChainContext<T> implements CommandLineRunner {
    
    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = Maps.newHashMap();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(String mark, T requestParam) {
        abstractChainHandlerContainer.get(mark).stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
        // 获取 Spring IOC 容器中所有 AbstractChainHandler 接口实现
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            if (abstractChainHandlers == null) {
                abstractChainHandlers = new ArrayList();
            }
            abstractChainHandlers.add(bean);
            // 根据 mark 标识将责任链模式分类,放入责任链容器上下文中
            abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);
        });
    }
}

run方法首先使用ioc容器获取所有接口实现类,然后流式编程,对每个类别的bean(接口实现类),判断是否已经将个类别组的list存入了待处理容器abstractChainHandlerContainer中,如果存入了(即list非空),则直接将这个bean添加到这个list中,如果未存入(即list为空),说明当前的bean是这个类别组的第一个,需要new一个list并将bean添加到list中,再将list存入待处理容器abstractChainHandlerContainer

抽象业务接口

image.png

继续探讨责任链子类的编写,只需要将之前的  OrderCreateChainHandler  实现接口改为  OrderCreateChainFilter  即可

// 订单创建参数必填检验
@Component
public final class OrderCreateParamNotNullChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
    	// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
}

// 订单创建参数正确性检验
@Component
public final class OrderCreateParamVerificationChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {
    
    @Override
    public void handler(OrderCreateCommand requestParam) {
    	// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 1;
    }
}

// 订单创建商品 SKU 库存验证
@Component
public final class OrderCreateProductSkuStockChainHandler implements OrderCreateChainFilter<OrderCreateCommand> {

    @Override
    public void handler(OrderCreateCommand requestParam) {
    	// 逻辑执行
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}

使用

@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final AbstractChainContext<OrderCreateCommand> abstractChainContext;
  
    public String createOrder(OrderCreateCommand requestParam) {
        // 责任链模式: 执行订单创建参数验证
        abstractChainContext.handler(OrderChainMarkEnum.ORDER_CREATE_FILTER.name(), requestParam);
    }
}

业务场景三:为用户注册设计一套校验规则

封装请求体

@Data
public class UserRegisterReqDTO {

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 真实姓名
     */
    private String realName;

    /**
     * 证件类型
     */
    private Integer idType;

    /**
     * 证件号
     */
    private String idCard;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 邮箱
     */
    private String mail;

    /**
     * 旅客类型
     */
    private Integer userType;

    /**
     * 审核状态
     */
    private Integer verifyState;

    /**
     * 邮编
     */
    private String postCode;

    /**
     * 地址
     */
    private String address;

    /**
     * 国家/地区
     */
    private String region;

    /**
     * 固定电话
     */
    private String telephone;
}

定义业务责任链接口

public interface UserRegisterCreateChainFilter<T extends UserRegisterReqDTO> extends AbstractChainHandler<UserRegisterReqDTO> {

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

当构建责任链时,为什么要定义具体业务责任链接口而不是直接实现抽象责任链类 AbstractChainHandler 呢?(责任链抽象部分 的 抽象业务接口 中的 抽象责任链过滤器也是这个原理

image.png

image.png

image.png

其实就是使得不同业务的责任链接口是独立的,只展示出该业务相应的实现类,如果一味使用AbstractChainHandler,则可能展示出大量不同业务的实现类,另外,可能有些业务不需要提供mark,即不实现mark接口,因此对责任链接口做具体设计而不是抽象设计更胜一筹

定义责任链业务具体处理器

image.png

// 验证参数必填
@Component
public final class UserRegisterParamNotNullChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqDTO> {

    @Override
    public void handler(UserRegisterReqDTO 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.getPhone())) {
            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);
        }
        // xxx 这里应该把所有用户注册入参校验必填,因为重复工作量所以暂时验证上述这些
    }

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

// 验证用户名是否可用
@Component
@RequiredArgsConstructor
public final class UserRegisterHasUsernameChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqDTO> {

    private final UserLoginService userLoginService;

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

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

// 验证证件号是否多次注销,如果是的话加入黑名单
@Component
@RequiredArgsConstructor
public final class UserRegisterCheckDeletionChainHandler implements UserRegisterCreateChainFilter<UserRegisterReqDTO> {

    private final UserService userService;

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

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

将以上各种handler分组存入待处理集合

public final class AbstractChainContext<T> implements CommandLineRunner {
    
    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = Maps.newHashMap();
    
    /**
     * 责任链组件执行
     *
     * @param requestParam 请求参数
     */
    public void handler(String mark, T requestParam) {
        abstractChainHandlerContainer.get(mark).stream()
                .sorted(Comparator.comparing(Ordered::getOrder)).forEach(each -> each.handler(requestParam));
    }
    
    @Override
    public void run(String... args) throws Exception {
        // 获取 Spring IOC 容器中所有 AbstractChainHandler 接口实现
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            if (abstractChainHandlers == null) {
                abstractChainHandlers = new ArrayList();
            }
            abstractChainHandlers.add(bean);
            // 根据 mark 标识将责任链模式分类,放入责任链容器上下文中
            abstractChainHandlerContainer.put(bean.mark(), abstractChainHandlers);
        });
    }
}

使用

@Slf4j
@Service
@RequiredArgsConstructor
public class UserLoginServiceImpl implements UserLoginService {

    private final AbstractChainContext<UserRegisterReqDTO> abstractChainContext;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserRegisterRespDTO register(UserRegisterReqDTO requestParam) {
        abstractChainContext.handler(UserChainMarkEnum.USER_REGISTER_FILTER.name(), requestParam);
    
        ...
    
    }
}