简介
定义:给多个对象处理请求的机会,减少请求的发送者与接受者之间的耦合。将接受对象链接起来,在链中传递请求,直到有一个对象处理这个请求。
速记:责任传递
案例:财务报销、击鼓传花、Sentinel(CtSph.java)、Zookeeper、Nacos 我考虑对创建订单的流程通过责任链模式的方式进行重构,先来看看我创建订单的流程。
创建订单 -> 消耗优惠券 -> 发货 -> 返利
环境介绍:
jdk 1.8 , spring 5.2.x
代码实现
代码实现入下图所示,通过 AbstractOrderHandler
定义抽象的接口,规范 Handler 的行为,在我的场景下有 4 个 Handler :
1、CreateOrderHandler 创建订单。
2、UseCouponOrderHandler 使用优惠券
3、GoodsDeliverOrderHandler 商品发货
4、RebateOrderHandler 营销返现
通过这样的设计我们就可以巧妙的,将繁杂的流程,进行水平拆分为 Handler ,将之前的 BIG Method ,拆分成了一个可以复用的低耦合的类文件。下面是一个类的示意图:
定义抽象方法
AbstractOrderHandler
定义如下,主要是有两个作用,定义 doHandle
抽象方法,以及为后期按照类型区分 Handler 业务的的 getTypeEnum
方法。
public abstract class AbstractOrderHandler {
/**
* 区分类型
*
* @return
*/
protected abstract OrderTypeEnum getTypeEnum();
/**
* 核心处理
*
* @param context 上下文
* @param args 拓展参数
*/
public void doHandle(OrderHandleContext context,
OrderHandlerChain chain, Object... args) {
// 我是否可以处理
if (Objects.isNull(getTypeEnum()) ||
Objects.equals(context.getTypeEnum(), getTypeEnum())) {
// 让我来处理
doHandle(context, args);
}
// 我处理完了,交给下家
chain.handle(context, args);
}
/**
* 具体业务处理
*
* @param context
* @param args
*/
protected abstract void doHandle(OrderHandleContext context, Object... args);
}
责任链的实现
具体的 Handler 实现,这里我列举了两个 Handler 的代码,分别是 CreateOrderHandler 创建订单、RebateOrderHandler 营销返利。核心逻辑即使实现 AbstractOrderHandler 接口,并且实现内部的细分逻辑。
// 创建订单
@Slf4j
@Service
@Order(100)
public class CreateOrderHandler extends AbstractOrderHandler {
@Override
protected OrderTypeEnum getTypeEnum() {
return null;
}
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
log.info("default create order ... ");
// 锁定库存
lockSku(context, args);
// 保存订单
saveOrder(context);
// 扣除库存
deductSku(context, args)
}
}
// 订单反现金
@Service
@Slf4j
@Order(200)
public class RebateOrderHandler extends AbstractOrderHandler {
@Override
protected OrderTypeEnum getTypeEnum() {
return null;
}
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
log.info("default rebate order ... ");
}
}
定义调用入口
OrderHandlerChain
是外部调用的入口,其实它主要的作用就是获取 AbstractOrderHandler 并且排序(即串联/编排 Handler ) 然后进行执行。这里我充分使用了 Spring 的 Bean 排序功能,通过在 Handler 上面定义 @Order 注解并且传入顺序值,我们在 @Autowired
获取 List 的时候,Spring 会给我自动注入排好序的 handlerList 。
@Slf4j
@Component
public class OrderHandlerChain {
@Autowired
private List<AbstractOrderHandler> chain;
@Autowired
private ApplicationContext applicationContext;
public void handle(OrderHandleContext context, Object... objects) {
if (context.getPos() < chain.size()) {
AbstractOrderHandler handler = chain.get(context.getPos());
// 移动位于处理器链中的位置
context.setPos(context.getPos() + 1);
handler.doHandle(context, this, objects);
}
}
}
业务拓展
如果我的订单逻辑发生变化,需要支持汽车订单的创建和兼容。我们可以增加 Car 处理的 handler 通过指定不同 OrderTypeEnum
进行处理,如果你不想创建更多的 handler 类文件也可以通过 @Bean
来进行实现。
这里其实也是一种妥协的方式,其实和直接实现 AbstractOrderHandler
并没有什么区别,都会生成 .class 文件,只是说在开发侧来看少了一个 Java 文件而已,也会占 JVM 的 Metaspace 空间。
如下所示:
@Configuration
public class CarOrderHandlers {
/**
* 汽车订单创建
*
* @return
*/
@Bean(name = "createOrderByCar")
public AbstractOrderHandler createOrderByCar() {
return new CreateOrderHandler() {
@Override
protected OrderTypeEnum getTypeEnum() {
return OrderTypeEnum.Car;
}
@Autowired
private ApplicationContext applicationContext;
@Override
protected void doHandle(OrderHandleContext context, Object... args) {
System.out.println("car order create ....");
}
};
}
}
测试代码
测试代码如下,我们只需要传入一个 Context 对象然后调用 chain.handle
方法即可。
@Slf4j
@SpringBootTest(classes = App.class)
public class OrderHandlerChainTest {
@Resource
private OrderHandlerChain chain;
@Test
public void testOrderChain() {
OrderHandleContext context = new OrderHandleContext();
context.setTypeEnum(OrderTypeEnum.Car);
chain.handle(context, null);
}
}
总结
本文主要是利用了 Spring 进行排序, Bean 定义等特征,实现责任链。感觉改造过后,有一点策略 + 模板 的感觉。策略模式主要是运用: 多方案切换的场景对业务进行垂直路由分别处理。责任链模式主要运用:责任传递的场景对业务进行水平分段处理粒度可以说更加细一些。
其实我们 JDK8 还提供了 @FunctionalInterface
函数接口,我们也可以将 AbstractOrderHandler
修改为 interface 接口,这样我们就可以通过 _lambda 表达式的方式注册 _Handler 其实本质都是一样的。Over!欢迎大家留言交流