文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

设计模式之责任链模式

2024-12-02 12:25

关注

速记:责任传递 

案例:财务报销、击鼓传花、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 方法。

  1. public abstract class AbstractOrderHandler { 
  2.  
  3.      
  4.     protected abstract OrderTypeEnum getTypeEnum(); 
  5.  
  6.      
  7.     public void doHandle(OrderHandleContext context, 
  8.                          OrderHandlerChain chain, Object... args) { 
  9.         // 我是否可以处理 
  10.         if (Objects.isNull(getTypeEnum()) ||  
  11.             Objects.equals(context.getTypeEnum(), getTypeEnum())) { 
  12.             // 让我来处理 
  13.             doHandle(context, args); 
  14.         } 
  15.         // 我处理完了,交给下家 
  16.         chain.handle(context, args); 
  17.     } 
  18.  
  19.      
  20.     protected abstract void doHandle(OrderHandleContext context, Object... args); 
  21.  

责任链的实现

具体的 Handler 实现,这里我列举了两个 Handler 的代码,分别是 CreateOrderHandler 创建订单、RebateOrderHandler 营销返利。核心逻辑即使实现 AbstractOrderHandler 接口,并且实现内部的细分逻辑。

  1. // 创建订单 
  2. @Slf4j 
  3. @Service 
  4. @Order(100) 
  5. public class CreateOrderHandler extends AbstractOrderHandler { 
  6.  
  7.     @Override 
  8.     protected OrderTypeEnum getTypeEnum() { 
  9.         return null
  10.     } 
  11.      
  12.     @Override 
  13.     protected void doHandle(OrderHandleContext context, Object... args) { 
  14.         log.info("default create order ... "); 
  15.  
  16.         // 锁定库存 
  17.         lockSku(context, args); 
  18.          
  19.         // 保存订单 
  20.         saveOrder(context); 
  21.          
  22.         // 扣除库存 
  23.         deductSku(context, args) 
  24.     } 
  25.  
  26.  
  27. // 订单反现金 
  28. @Service 
  29. @Slf4j 
  30. @Order(200) 
  31. public class RebateOrderHandler extends AbstractOrderHandler { 
  32.  
  33.     @Override 
  34.     protected OrderTypeEnum getTypeEnum() { 
  35.         return null
  36.     } 
  37.  
  38.     @Override 
  39.     protected void doHandle(OrderHandleContext context, Object... args) { 
  40.         log.info("default rebate order ... "); 
  41.     } 

定义调用入口

OrderHandlerChain 是外部调用的入口,其实它主要的作用就是获取 AbstractOrderHandler 并且排序(即串联/编排 Handler ) 然后进行执行。这里我充分使用了 Spring 的 Bean 排序功能,通过在 Handler 上面定义 @Order 注解并且传入顺序值,我们在 @Autowired 获取 List 的时候,Spring 会给我自动注入排好序的 handlerList 。

  1. @Slf4j 
  2. @Component 
  3. public class OrderHandlerChain { 
  4.  
  5.     @Autowired 
  6.     private List chain; 
  7.     @Autowired 
  8.     private ApplicationContext applicationContext; 
  9.  
  10.     public void handle(OrderHandleContext context, Object... objects) { 
  11.         if (context.getPos() < chain.size()) { 
  12.             AbstractOrderHandler handler = chain.get(context.getPos()); 
  13.             // 移动位于处理器链中的位置 
  14.             context.setPos(context.getPos() + 1); 
  15.             handler.doHandle(context, this, objects); 
  16.         } 
  17.     } 
  18.  

业务拓展

如果我的订单逻辑发生变化,需要支持汽车订单的创建和兼容。我们可以增加 Car 处理的 handler 通过指定不同 OrderTypeEnum 进行处理,如果你不想创建更多的 handler 类文件也可以通过 @Bean 来进行实现。

这里其实也是一种妥协的方式,其实和直接实现 AbstractOrderHandler 并没有什么区别,都会生成 .class 文件,只是说在开发侧来看少了一个 Java 文件而已,也会占 JVM 的 Metaspace 空间。

如下所示:

  1. @Configuration 
  2. public class CarOrderHandlers { 
  3.  
  4.      
  5.     @Bean(name = "createOrderByCar"
  6.     public AbstractOrderHandler createOrderByCar() { 
  7.         return new CreateOrderHandler() { 
  8.             @Override 
  9.             protected OrderTypeEnum getTypeEnum() { 
  10.                 return OrderTypeEnum.Car; 
  11.             } 
  12.  
  13.             @Autowired 
  14.             private ApplicationContext applicationContext; 
  15.  
  16.             @Override 
  17.             protected void doHandle(OrderHandleContext context, Object... args) { 
  18.                 System.out.println("car order create ...."); 
  19.             } 
  20.         }; 
  21.     } 
  22.  

测试代码

测试代码如下,我们只需要传入一个 Context 对象然后调用 chain.handle 方法即可。

  1. @Slf4j 
  2. @SpringBootTest(classes = App.class) 
  3. public class OrderHandlerChainTest { 
  4.  
  5.     @Resource 
  6.     private OrderHandlerChain chain; 
  7.  
  8.     @Test 
  9.     public void testOrderChain() { 
  10.  
  11.         OrderHandleContext context = new OrderHandleContext(); 
  12.         context.setTypeEnum(OrderTypeEnum.Car); 
  13.         chain.handle(context, null); 
  14.     } 

总结

本文主要是利用了 Spring 进行排序, Bean 定义等特征,实现责任链。感觉改造过后,有一点策略 + 模板 的感觉。策略模式主要是运用: 多方案切换的场景对业务进行垂直路由分别处理。责任链模式主要运用:责任传递的场景对业务进行水平分段处理粒度可以说更加细一些。

其实我们 JDK8 还提供了 @FunctionalInterface 函数接口,我们也可以将 AbstractOrderHandler 修改为 interface 接口,这样我们就可以通过 lambda 表达式的方式注册 Handler 其实本质都是一样的。Over!欢迎大家留言交流。

参考文章

https://www.cnblogs.com/vcmq/p/12542399.html

http://c.biancheng.net/view/1383.html

 

来源:运维开发故事内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯