设计模式之责任链模式

简介: 设计模式之责任链模式

简介


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


速记责任传递


案例:财务报销、击鼓传花、Sentinel(CtSph.java)、Zookeeper、Nacos


640.png


我考虑对创建订单的流程通过责任链模式的方式进行重构,先来看看我创建订单的流程。


创建订单 -> 消耗优惠券 -> 发货 ->


环境介绍:


jdk 1.8 , spring 5.2.x


代码实现


代码实现如下图所示,通过 AbstractOrderHandler 定义抽象的接口,规范 Handler 的行为,在我的场景下有 4 个 Handler : 1、CreateOrderHandler 创建订单。2、UseCouponOrderHandler 使用优惠券 3、GoodsDeliverOrderHandler 商品发货 4、RebateOrderHandler 营销返现 通过这样的设计我们就可以巧妙的,将繁杂的流程,进行水平拆分为 Handler ,将之前的 BIG Method ,拆分成了一个可以复用的低耦合的类文件。下面是一个类的示意图:


640.png


定义抽象方法


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!欢迎大家留言交流。

相关文章
|
10月前
|
设计模式 Java
【设计模式系列笔记】责任链模式
责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理它。每个处理者都有一个对下一个处理者的引用。责任链模式常用于处理请求的场景,例如在一个请求需要经过多个步骤或者多个对象来处理的情况下。
109 0
|
10月前
|
设计模式 Java
【设计模式】责任链模式
【设计模式】责任链模式
|
10月前
|
设计模式
【设计模式】责任链模式
【1月更文挑战第27天】【设计模式】责任链模式
|
1月前
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。
61 13
「全网最细 + 实战源码案例」设计模式——责任链模式
|
4月前
|
设计模式 JavaScript Scala
Kotlin - 改良设计模式 - 责任链模式
Kotlin - 改良设计模式 - 责任链模式
58 3
|
5月前
|
设计模式 JavaScript Scala
Kotlin - 改良设计模式 - 责任链模式
Kotlin - 改良设计模式 - 责任链模式
71 9
|
4月前
|
设计模式 JavaScript Scala
Kotlin教程笔记(55) - 改良设计模式 - 责任链模式
Kotlin教程笔记(55) - 改良设计模式 - 责任链模式
45 0
|
4月前
|
设计模式 JavaScript Scala
Kotlin教程笔记(55) - 改良设计模式 - 责任链模式
Kotlin教程笔记(55) - 改良设计模式 - 责任链模式
53 0
|
5月前
|
设计模式 JavaScript Scala
Kotlin - 改良设计模式 - 责任链模式
本教程详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。对于希望快速上手Kotlin的读者,推荐查阅“简洁”系列教程。本文通过学生会经费申请的例子,介绍了责任链模式及其在Kotlin中的实现,并使用偏函数进行了改进,使代码更加简洁和灵活。
30 0
|
5月前
|
设计模式 JavaScript Scala
Kotlin - 改良设计模式 - 责任链模式
Kotlin - 改良设计模式 - 责任链模式
52 0