設(shè)計模式之責任鏈模式
簡介
定義:給多個對象處理請求的機會,減少請求的發(fā)送者與接受者之間的耦合。將接受對象鏈接起來,在鏈中傳遞請求,直到有一個對象處理這個請求。
速記:責任傳遞
案例:財務(wù)報銷、擊鼓傳花、Sentinel(CtSph.java)、Zookeeper、Nacos
我考慮對創(chuàng)建訂單的流程通過責任鏈模式的方式進行重構(gòu),先來看看我創(chuàng)建訂單的流程。
創(chuàng)建訂單 -> 消耗優(yōu)惠券 -> 發(fā)貨 -> 返利
環(huán)境介紹:
jdk 1.8 , spring 5.2.x
代碼實現(xiàn)
代碼實現(xiàn)如下圖所示,通過 AbstractOrderHandler 定義抽象的接口,規(guī)范 Handler 的行為,在我的場景下有 4 個 Handler : 1、CreateOrderHandler 創(chuàng)建訂單。2、UseCouponOrderHandler 使用優(yōu)惠券 3、GoodsDeliverOrderHandler 商品發(fā)貨 4、RebateOrderHandler 營銷返現(xiàn) 通過這樣的設(shè)計我們就可以巧妙的,將繁雜的流程,進行水平拆分為 Handler ,將之前的 BIG Method ,拆分成了一個可以復用的低耦合的類文件。下面是一個類的示意圖:
定義抽象方法
AbstractOrderHandler 定義如下,主要是有兩個作用,定義 doHandle 抽象方法,以及為后期按照類型區(qū)分 Handler 業(yè)務(wù)的的 getTypeEnum 方法。
- public abstract class AbstractOrderHandler {
- /**
- * 區(qū)分類型
- *
- * @return
- */
- protected abstract OrderTypeEnum getTypeEnum();
- /**
- * 核心處理
- *
- * @param context 上下文
- * @param args 拓展參數(shù)
- */
- 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);
- }
- /**
- * 具體業(yè)務(wù)處理
- *
- * @param context
- * @param args
- */
- protected abstract void doHandle(OrderHandleContext context, Object... args);
- }
責任鏈的實現(xiàn)
具體的 Handler 實現(xiàn),這里我列舉了兩個 Handler 的代碼,分別是 CreateOrderHandler 創(chuàng)建訂單、RebateOrderHandler 營銷返利。核心邏輯即使實現(xiàn) AbstractOrderHandler 接口,并且實現(xiàn)內(nèi)部的細分邏輯。
- // 創(chuàng)建訂單
- @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)
- }
- }
- // 訂單反現(xiàn)金
- @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 ... ");
- }
- }
定義調(diào)用入口
OrderHandlerChain 是外部調(diào)用的入口,其實它主要的作用就是獲取 AbstractOrderHandler 并且排序(即串聯(lián)/編排 Handler ) 然后進行執(zhí)行。這里我充分使用了 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);
- }
- }
- }
業(yè)務(wù)拓展
如果我的訂單邏輯發(fā)生變化,需要支持汽車訂單的創(chuàng)建和兼容。我們可以增加 Car 處理的 handler 通過指定不同 OrderTypeEnum 進行處理,如果你不想創(chuàng)建更多的 handler 類文件也可以通過 @Bean 來進行實現(xiàn)。
這里其實也是一種妥協(xié)的方式,其實和直接實現(xiàn) AbstractOrderHandler 并沒有什么區(qū)別,都會生成 .class 文件,只是說在開發(fā)側(cè)來看少了一個 Java 文件而已,也會占 JVM 的 Metaspace 空間。
如下所示:
- @Configuration
- public class CarOrderHandlers {
- /**
- * 汽車訂單創(chuàng)建
- *
- * @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 對象然后調(diào)用 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);
- }
- }
總結(jié)
本文主要是利用了 Spring 進行排序, Bean 定義等特征,實現(xiàn)責任鏈。感覺改造過后,有一點策略 + 模板 的感覺。策略模式主要是運用: 多方案切換的場景對業(yè)務(wù)進行垂直路由分別處理。責任鏈模式主要運用:責任傳遞的場景對業(yè)務(wù)進行水平分段處理粒度可以說更加細一些。
其實我們 JDK8 還提供了 @FunctionalInterface 函數(shù)接口,我們也可以將 AbstractOrderHandler 修改為 interface 接口,這樣我們就可以通過 lambda 表達式的方式注冊 Handler 其實本質(zhì)都是一樣的。Over!歡迎大家留言交流。
參考文章
https://www.cnblogs.com/vcmq/p/12542399.html
http://c.biancheng.net/view/1383.html