设计模式(二)责任链模式

99 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情

前言

上篇讲到设计模式之模版模式加载类,这一篇主要是以我在开发过程中,写业务代码所用到的另外一种设计模式—责任链模式,它与策略模式有点类似,可以避免我们少写一些if…else判断,还有switch语句这种。相信大家有时候看到,代码里面嵌套的一坨又一坨的if…else多少会有些头大,这时候就需要我们学习的一些设计模式来使我们代码的可读性、可扩展性、可维护性变强,所谓前人挖坑后人填坑,前人造福后人享福,我们一定要争取做造福的人。

业务场景

在订单系统里,由于业务的原因订单会有多种类型,比如有订单A、订单B、订单C,我们需要根据不同的订单类型,对接下游结算服务生成不同的计费单,并且订单类型都属于每一个节点,一个节点的业务逻辑走完才会接着走到下一节点,每种类型的订单就会有组装数据、对接结算服务接口、对接成功后会去更新自己的主表。

7C8D77AB-B233-426A-9F8A-CC9DCB15BC23.png

如果按常规的写法去实现这个需求,那么就要分别写三个接口,分别为订单类型为A的、B的、C的,并且都实现对应的三个方法,然后在调用的时候通过用if判断类型,最终去调用哪个service。 这样来做的话我们接口的耦合度就非常低,如果业务有改动,那么这三个接口的方法都要进行改动,这样改动量就有点大了,同时测试的工作量也会增长。 随着我们业务的拓展,会有很多订单类型,如果每加一种订单类型就去写一个接口的话,那样项目的体积会非常大,同时也会有很多重复的代码逻辑,整个项目会变得非常臃肿。

责任链模式

有链的概念说明,链中是有序的,或者说是有多个节点的,每个节点处理完成后会传递给下一个节点,一环扣一环的。

在责任链中其实只包含着两种角色:

  1. Handler:公共的处理类,解藕了所有请求的公共方法,将公共方法进行抽象。
  2. Next(下一个处理者):在链表的数据结构也是有对应的next节点,也是一个独立的对象,完成着对应节点所要做的事情,让请求能够有效的传递到下一个节点。

具体实现

OrderHandler抽象订单公共处理类

@Data
public abstract class OrderHandler {

    /**
     * 下一个处理类
     */
    private OrderHandler next;

    /**
     * 获取数据
     */
    public abstract void getOrderData();

    /**
     * 节点传递方法
     */
    public void getOrderDataProcess() {
        this.getOrderData();
        if (null != next) {
            next.getOrderDataProcess();
        }
    }

    /**
     * 请求接口
     */
    public abstract void getRequestClient();

    /**
     * 请求接口节点传递方法
     */
    public void getRequestClientProcess() {
        this.getRequestClient();
        if (null != next) {
            next.getRequestClientProcess();
        }
    }

    /**
     * 更新结果
     */
    public abstract void updateResult();

    /**
     * 传递下一节点进行更更新
     */
    public void updateResultProcess() {
        this.updateResult();
        if (null != next) {
            next.updateResultProcess();
        }
    }
}

OrderHandlerClient 责任链阶段处理类

@Component
@AllArgsConstructor
public class OrderHandlerClient {

    private OrderTypeAHandler orderTypeAHandler;
    private OrderTypeBHandler orderTypeBHandler;
    private OrderTypeCHandler orderTypeCHandler;

    /**
     * 处理类节点链路 顺序为 A->B->C
     * @return
     */
    public OrderHandler getInitFirstHandler(){
        orderTypeAHandler.setNext(orderTypeBHandler);
        orderTypeBHandler.setNext(orderTypeCHandler);
        return orderTypeAHandler;
    }
}

OrderTypeAHandler 订单类型为A的链路对象

@Component
@Slf4j
public class OrderTypeAHandler extends OrderHandler{

    @Autowired
    private RequestUtil requestUtil;
    @Autowired
    private ResultUtil resultUtil; 
    
    @Override
    public void getOrderData() {
        log.info("订单类型为A--------组装数据");
    }

    @Override
    public void getRequestClient() {
        requestUtil.request(OrderA.class);
    }

    @Override
    public void updateResult() {
        resultUtil.updateResult(OrderA.class);
    }  
}

这里只写一个,类型为B、C的同A一样,继承OrderHandler抽象类即可。

ResultUtil更新结果泛型类

@Slf4j
@Component
public class ResultUtil<T,K> {

    //范型类,可以通过传不同的类,以及数据更新的mapper
    // 进行反射 ,来对不同订单类的表进行更新
    public void updateResult(Class<T> tClass){
        log.info("公共更新的结果方法~~~~~~~~~·");
        //反射获取对应的处理类
        T t = ReflectUtil.newInstance(tClass);
        log.info("处理类:"+t.getClass().getName());
    }
}

RequestUtil请求接口公共工具类

@Slf4j
@Component
public class RequestUtil<T,K>{

    public void request(Class<T> tClass){
        log.info("公共调用接口的方法~~~~~~~~~·");
        //反射获取对应的处理类
        T t = ReflectUtil.newInstance(tClass);
        log.info("处理类:"+t.getClass().getName());
    }
}

测试类

@RestController
@AllArgsConstructor
public class ChainTest {

    private OrderHandlerClient orderHandlerClient;

    @GetMapping("/test")
    public void chainTestMethod(){
        OrderHandler orderFeeHandler = orderHandlerClient.getInitFirstHandler();
        //获取数据
        orderFeeHandler.getOrderDataProcess();
        //调用接口
        orderFeeHandler.getRequestClientProcess();
        //更新结果
        orderFeeHandler.updateResultProcess();
    }
}

测试结果

由此可见整个处理链路,按照我在OrderHandlerClient链路处理类的节点顺序在执行,并且在某个节点执行了业务逻辑方法,包括获取数据、调用接口、更新数据。

39C7F677-2386-42E3-9A47-38D7BD525C15.png

总结

以上演示了整个责任链的具体实现,可以看到整个调用链是不是非常清晰呢,代码的结构也是相对清晰的,优点显而易见。但是其实也是有缺点的,这里包括可能某个节点的处理时间过长,会影响整体的性能,还有就是节点不要循环引用,从而出现死循环,造成服务挂掉。合理使用设计模式,使我们的代码事半功倍。