开篇
提示:阅读本文可以了解到一下内容
- 结合具体案例了解责任链模式的使用场景
- 使用责任链模式实现流程编排以及动态扩展
- 使用Spring @Resource注解的骚操作
- 递归设置责任链路
前言
责任链模式,顾名思义,就是将多个对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。请求在链路上传递,链路上的每个对象就是一个处理器,每个处理器都可以对请求进行处理,或者直接传递给下一个处理器处理。
应用场景
责任链模式在实际工作中主要有以下两个应用场景:
- 多重检验。操作需要经过一系列的校验,通过校验后才执行某些操作
- 工作流。类似企业的OA系统会制定很多流程,一级一级的去处理任务
下面我们通过一个案例来了解一下责任链模式。
案例-创建商品的多重校验场景
以创建商品为例,假设商品创建逻辑分为以下三步完成:
- 创建商品
- 校验商品参数
- 保存商品
第2步校验商品又分为多种情况的校验,必填字段校验、规格校验、价格校验、库存校验等等。这些检验逻辑像一个流水线,要想创建出一个商品,必须通过这些校验。
伪代码如下:
if(nullCheck(param)) throw Exception
if(specCheck(param)) throw Exception
if(stocCheck(param)) throw Exception
return true;
如上代码看起来非常工整且逻辑清晰,但随着业务需求不断增加,相关的校验逻辑也越来越多,越来越复杂,可能使代码非常臃肿且不易于维护。更糟糕的是这些校验的组件不可复用,当有其它需求需要用到一些校验的时候,系统的维护成本也越来越高。
接下来我们使用责任链模式进行优化:创建商品的每一个校验步骤都可以作为一个单独的校验服务,抽离出一个类,便于复用。这些服务形成一个链式调用,请求在处理链上传递,如果校验不通过,则处理器不再向下传递请求,直接返回错误或者抛出异常;若所有校验都通过,那么则执行最后的业务处理比如保存商品。
二、案例实战:责任链模式实现创建商品校验
1.UML图
AbstractCheckHandler 表示处理器抽象类,负责抽象处理器行为。其有3个子类,分别是:
- NullValueCheckHandler:空值校验处理器
- PriceCheckHandler:价格校验处理
- StockCheckHandler:库存校验处理器
AbstractCheckHandler抽象类中, handle()定义了处理器的抽象方法,其子类需要重写handle()方法以实现特殊的处理器校验逻辑;
protected ProductCheckHandlerConfig config 是处理器的动态配置类,使用protected声明,每个子类处理器都持有该对象。该对象用于声明当前处理器、以及当前处理器的下一个处理器nextHandler,另外也可以配置一些特殊属性,比如说接口降级配置、超时时间配置等。
AbstractCheckHandler nextHandler 是当前处理器持有的下一个处理器的引用,当前处理器执行完毕时,便调用nextHandler执行下一处理器的handle()校验方法;
protected Result next() 是抽象类中定义的,执行下一个处理器的方法,使用protected声明,每个子类处理器都持有该对象。当子类处理器执行完毕(通过)时,调用父类的方法执行下一个处理器nextHandler。
HandlerClient 是执行处理器链路的客户端,HandlerClient.executeChain()方法负责发起整个链路调用,并接收处理器链路的返回值。
2.商品参数对象:保存商品的入参
ProductVO是创建商品的参数对象,包含商品的基础信息
/**
* 商品对象
*/
@Data
@Builder
public class ProductVO {
/**
* 商品SKU,唯一
*/
private Long skuId;
/**
* 商品名称
*/
private String skuName;
/**
* 商品图片路径
*/
private String imgPath;
/**
* 价格
*/
private BigDecimal price;
/**
* 库存
*/
private Integer stock;
}
AbstractCheckHandler :处理器抽象类,并使用@Component注解注册为由Spring管理的Bean对象,这样做的好处是,我们可以轻松的使用Spring来管理这些处理器Bean。
/**
* 抽象类处理器
*/
@Component
public abstract class AbstractCheckHandler {
/**
* 当前处理器持有下一个处理器的引用
*/
@Getter
@Setter
private AbstractCheckHandler nextHandler;
/**
* 处理器执行方法
* @param param
* @return
*/
public abstract Result handle(ProductVO param);
/**
* 处理器配置
*/
@Setter
@Getter
protected ProductCheckHandlerConfig config;
/**
* 链路传递
* @param param
* @return
*/
protected Result next(ProductVO param) {
//下一个链路没有处理器了,直接返回
if (Objects.isNull(nextHandler)) {
return Result.success();
}
//执行下一个处理器
return nextHandler.handle(param);
}
}
ProductCheckHandlerConfig 配置类:
/**
* 处理器配置类
*/
@AllArgsConstructor
@Data
public class ProductCheckHandlerConfig {
/**
* 处理器Bean名称
*/
private String handler;
/**
* 下一个处理器
*/
private ProductCheckHandlerConfig next;
/**
* 是否降级
*/
private Boolean down = Boolean.FALSE;
}
AbstractCheckHandler抽象类处理器有3个子类分别是:
- NullValueCheckHandler:空值校验处理器
- PriceCheckHandler:价格校验处理
- StockCheckHandler:库存校验处理器
同样,使用@Component注册为由Spring管理的Bean对象
/**
* 空值校验处理器
*/
@Component
public class NullValueCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println("空值校验 Handler 开始...");
//降级:如果配置了降级,则跳过此处理器,执行下一个处理器
if (super.getConfig().getDown()) {
System.out.println("空值校验 Handler 已降级,跳过空值校验 Handler...");
return super.next(param);
}
//参数必填校验
if (Objects.isNull(param)) {
return Result.failure(ErrorCode.PARAM_NULL_ERROR);
}
//SkuId商品主键参数必填校验
if (Objects.isNull(param.getSkuId())) {
return Result.failure(ErrorCode.PARAM_SKU_NULL_ERROR);
}
//Price价格参数必填校验
if (Objects.isNull(param.getPrice())) {
return Result.failure(ErrorCode.PARAM_PRICE_NULL_ERROR);
}
//Stock库存参数必填校验
if (Objects.isNull(param.getStock())) {
return Result.failure(ErrorCode.PARAM_STOCK_NULL_ERROR);
}
System.out.println("空值校验 Handler 通过...");
//执行下一个处理器
return super.next(param);
}
}
/**
* 价格校验处理器
*/
@Component
public class PriceCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println("价格校验 Handler 开始...");
//非法价格校验
boolean illegalPrice = param.getPrice().compareTo(BigDecimal.ZERO) <= 0;
if (illegalPrice) {
return Result.failure(ErrorCode.PARAM_PRICE_ILLEGAL_ERROR);
}
//其他校验逻辑...
System.out.println("价格校验 Handler 通过...");
//执行下一个处理器
return super.next(param);
}
}
/**
* 库存校验处理器
*/
@Component
public class StockCheckHandler extends AbstractCheckHandler{
@Override
public Result handle(ProductVO param) {
System.out.println("库存校验 Handler 开始...");
//非法库存校验
boolean illegalStock = param.getStock() < 0;
if (illegalStock) {
return Result.failure(ErrorCode.PARAM_STOCK_ILLEGAL_ERROR);
}
//其他校验逻辑..
System.out.println("库存校验 Handler 通过...");
//执行下一个处理器
return super.next(param);
}
}
HandlerClient客户端类负责发起整个处理器链路的执行,通过executeChain()方法。如果处理器链路返回错误信息,即校验未通过,则整个链路截断(停止),返回相应的错误信息。
/**
* 责任链模式之客户端
*/
public class HandlerClient {
/**
* 执行链路
* @param handler 处理器
* @param param 商品参数
* @return
*/
public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
//执行处理器
Result handlerResult = handler.handle(param);
if (!handlerResult.isSuccess()) {
System.out.println("HandlerClient 责任链执行失败返回:" + handlerResult.toString());
return handlerResult;
}
return Result.success();
}
}
以上,责任链模式相关的类已经创建好了。接下来就可以创建商品了。
@Component
public class ProductService {
/**
* 使用Spring注入:所有继承了AbstractCheckHandler抽象类的Spring Bean都会注入进来。
* Map的Key对应Bean的name,Value是name对应相应的Bean
*/
@Resource
private Map<String, AbstractCheckHandler> handlerMap;
/**
* 创建商品
* @return
*/
public Result createProduct(ProductVO param) {
//参数校验,使用责任链模式
Result paramCheckResult = this.paramCheckChain(param);
if (!paramCheckResult.isSuccess()) {
return paramCheckResult;
}
//创建商品
return this.saveProduct(param);
}
/**
* 参数校验:责任链模式
* @param param
* @return
*/
private Result paramCheckChain(ProductVO param) {
//获取处理器配置:通常配置使用统一配置中心存储,支持动态变更
ProductCheckHandlerConfig handlerConfig = this.getHandlerConfigFile();
//获取处理器
AbstractCheckHandler handler = this.getHandler(handlerConfig);
//责任链:执行处理器链路
Result executeChainResult = HandlerClient.executeChain(handler, param);
if (!executeChainResult.isSuccess()) {
System.out.println("创建商品 失败...");
return executeChainResult;
}
//处理器链路全部成功
return Result.success();
}
/**
* 获取处理器配置:通常配置使用统一配置中心存储,支持动态变更
* @return
*/
private ProductCheckHandlerConfig getHandlerConfigFile() {
//配置中心存储的配置
String configJson = "{\"handler\":\"nullValueCheckHandler\",\"down\":false,\"next\":{\"handler\":\"priceCheckHandler\",\"next\":{\"handler\":\"stockCheckHandler\",\"next\":null}}}";
//转成Config对象
ProductCheckHandlerConfig handlerConfig = JSON.parseObject(configJson, ProductCheckHandlerConfig.class);
return handlerConfig;
}
/**
* 获取处理器
* @param config
* @return
*/
private AbstractCheckHandler getHandler (ProductCheckHandlerConfig config) {
//配置检查:没有配置处理器链路,则不执行校验逻辑
if (Objects.isNull(config)) {
return null;
}
//配置错误
String handler = config.getHandler();
if (StringUtils.isBlank(handler)) {
return null;
}
//配置了不存在的处理器
AbstractCheckHandler abstractCheckHandler = handlerMap.get(config.getHandler());
if (Objects.isNull(abstractCheckHandler)) {
return null;
}
//处理器设置配置Config
abstractCheckHandler.setConfig(config);
//递归设置链路处理器
abstractCheckHandler.setNextHandler(this.getHandler(config.getNext()));
return abstractCheckHandler;
}
private Result saveProduct(ProductVO param) {
System.out.println("保存商品 成功...");
return Result.success(param);
}
}
3.测试代码执行结果
@SpringBootTest
class DesignDemoChainApplicationTests {
@Resource
ProductService productService;
@Resource
ApproveService approveService;
@Test
void contextLoads() {
ProductVO param = ProductVO.builder()
.skuId(123L)
.skuName("测试商品")
.imgPath("http://..")
.price(new BigDecimal(-1))
.stock(1)
.build();
productService.createProduct(param);
}
}
总结
在实际应用中使用责任链模式有以下优点:
- 降低耦合度,分离请求和处理
- 扩展性高,可以动态新增和删除处理器
- 代码复用
在实际应用中使用责任链模式有以下缺点:
- 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
- 可能不容易观察运行时的特征,有碍于除错