设计模式之责任链模式

开发 前端
给多个对象处理请求的机会,减少请求的发送者与接受者之间的耦合。将接受对象链接起来,在链中传递请求,直到有一个对象处理这个请求。

[[441814]]

简介

定义:给多个对象处理请求的机会,减少请求的发送者与接受者之间的耦合。将接受对象链接起来,在链中传递请求,直到有一个对象处理这个请求。 

速记:责任传递 

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

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

责任链的实现

具体的 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 ... "); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

定义调用入口

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); 
        } 
    } 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

业务拓展

如果我的订单逻辑发生变化,需要支持汽车订单的创建和兼容。我们可以增加 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 ...."); 
            } 
        }; 
    } 
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

测试代码

测试代码如下,我们只需要传入一个 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); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

总结

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

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

参考文章

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

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

 

责任编辑:武晓燕 来源: 运维开发故事
相关推荐

2010-04-01 09:10:03

PHP设计模式责任链模式

2012-03-28 13:28:56

Java设计模式

2023-06-05 07:55:31

2024-01-30 13:15:00

设计模式责任链

2020-11-17 09:32:57

设计模式责任链

2023-09-26 00:27:07

设计模式链接

2022-12-28 08:08:57

2021-08-14 08:17:49

Android设计模式OKHttp

2022-11-01 08:46:20

责任链模式对象

2024-05-09 12:17:00

责任链设计模式

2024-06-04 13:11:52

Python行为设计模式开发

2024-12-03 15:52:45

责任链Java

2021-07-14 10:08:30

责任链模式加工链

2023-09-28 08:45:56

开源责任链模式

2021-06-05 17:59:00

责任链模式设计

2020-08-21 07:23:50

工厂模式设计

2021-06-09 08:53:34

设计模式策略模式工厂模式

2012-02-29 09:41:14

JavaScript

2021-05-25 09:00:52

责任链模式设计模式面试经历

2021-06-29 08:54:23

设计模式代理模式远程代理
点赞
收藏

51CTO技术栈公众号