责任链模式,简而言之,就是将多个操作组装成一条链路进行处理。请求在链路上传递,链路上的每一个节点就是一个处理器,每个处理器都可以对请求进行处理,或者传递给链路上的下一个处理器处理。
应用场景
责任链模式的应用场景,在实际工作中,通常有如下两种应用场景。
-
操作需要经过一系列的校验,通过校验后才执行某些操作。
-
工作流。企业中通常会制定很多工作流程,一级一级的去处理任务。
下面通过两个案例来学习一下责任链模式。
案例一:创建商品多级校验场景
以创建商品为例,假设商品创建逻辑分为以下三步完成:①创建商品、②校验商品参数、③保存商品。
第②步校验商品又分为多种情况的校验,必填字段校验、规格校验、价格校验、库存校验等等。这些检验逻辑像一个流水线,要想创建出一个商品,必须通过这些校验。如下流程图所示:
伪代码如下:
创建商品步骤,需要经过一系列的参数校验,如果参数校验失败,直接返回失败的结果;通过所有的参数校验后,最终保存商品信息。
如上代码看起来似乎没什么问题,它非常工整,而且代码逻辑很清晰。
PS:我没有把所有的校验代码都罗列在一个方法里,那样更能产生对比性,但我觉得抽象并分离单一职责的函数应该是每个程序员最基本的规范!
但是随着业务需求不断地叠加,相关的校验逻辑也越来越多,新的功能使代码越来越臃肿,可维护性较差。更糟糕的是,这些校验组件不可复用,当你有其他需求也需要用到一些校验时,你又变成了Ctrl+C , Ctrl+V程序员,系统的维护成本也越来越高。如下图所示:
伪代码同上,这里就不赘述了。
终于有一天,你忍无可忍了,决定重构这段代码。
使用责任链模式优化:创建商品的每个校验步骤都可以作为一个单独的处理器,抽离为一个单独的类,便于复用。这些处理器形成一条链式调用,请求在处理器链上传递,如果校验条件不通过,则处理器不再向下传递请求,直接返回错误信息;若所有的处理器都通过检验,则执行保存商品步骤。
案例一实战:责任链模式实现创建商品校验
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()
方法负责发起整个链路调用,并接收处理器链路的返回值。
撸起袖子开始撸代码吧 🤓 ~
商品参数对象:保存商品的入参
ProductVO是创建商品的参数对象,包含商品的基础信息。并且其作为责任链模式中多个处理器的入参,多个处理器都以ProductVO为入参进行特定的逻辑处理。实际业务中,商品对象特别复杂。咱们化繁为简,简化商品参数如下:
/**
* 商品对象
*/
@Data
@Builder
public class ProductVO {
/**
* 商品SKU,唯一
*/
private Long skuId;
/**
* 商品名称
*/
private String skuName;
/**
* 商品图片路径
*/
private String Path;
/**
* 价格
*/
private BigDecimal price;
/**
* 库存
*/
private Integer stock;
}
抽象类处理器:抽象行为,子类共有属性、方法
AbstractCheckHandler:处理器抽象类,并使用@Component注解注册为由Spring管理的Bean对象,这样做的好处是,我们可以轻松的使用Spring来管理这些处理器Bean。
/**
* 抽象类处理器
*/
@Component
public abstract class AbstractCheckHandler {
/**
* 当前处理器持有下一个处理器的引用
*/
@Getter
@Setter
protected AbstractCheckHandler nextHandler;
/**
* 处理器配置
*/
@Setter
@Getter
protected ProductCheckHandlerConfig config;
/**
* 处理器执行方法
* @param param
* @r