美文网首页互联网科技Java
太牛了,竟然用设计模式来干掉 if-else

太牛了,竟然用设计模式来干掉 if-else

作者: 互联网Java进阶架构 | 来源:发表于2020-07-02 14:12 被阅读0次

    前言

    物流行业中,通常会涉及到EDI报文(XML格式文件)传输和回执接收,每发送一份EDI报文,后续都会收到与之关联的回执(标识该数据在第三方系统中的流转状态)。

    这里枚举几种回执类型:MT1101、MT2101、MT4101、MT8104、MT8105、MT9999,系统在收到不同的回执报文后,会执行对应的业务逻辑处理。当然,实际业务场景并没有那么笼统,这里以回执处理为演示案例

    模拟一个回执类

     模拟一个回执生成器

    传统做法-if-else分支

    在遇到if-else的分支业务逻辑比较复杂时,我们都习惯于将其抽出一个方法或者封装成一个对象去调用,这样整个if-else结构就不会显得太臃肿。

    就上面例子,当回执的类型越来越多时,分支else if 就会越来越多,每增加一个回执类型,就需要修改或添加if-else分支,违反了开闭原则(对扩展开放,对修改关闭)

    策略模式+Map字典

    我们知道, 策略模式的目的是封装一系列的算法,它们具有共性,可以相互替换,也就是说让算法独立于使用它的客户端而独立变化,客户端仅仅依赖于策略接口 。

    在上述场景中,我们可以把if-else分支的业务逻辑抽取为各种策略,但是不可避免的是依然需要客户端写一些if-else进行策略选择的逻辑,我们可以将这段逻辑抽取到工厂类中去,这就是策略模式+简单工厂,代码如下

    策略接口

    策略接口实现类,也就是具体的处理者

    策略上下文类(策略接口的持有者

    策略工厂

    客户端

    public class Client {

        public static void main(String[] args) {

            //模拟回执

            List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();

            //策略上下文

            ReceiptStrategyContext receiptStrategyContext = new ReceiptStrategyContext();

            for (Receipt receipt : receiptList) {

                //获取并设置策略

                IReceiptHandleStrategy receiptHandleStrategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType());

                receiptStrategyContext.setReceiptHandleStrategy(receiptHandleStrategy);

                //执行策略

                receiptStrategyContext.handleReceipt(receipt);

            }

        }

    }

    解析报文MT2101:我是MT2101回执报文喔

    解析报文MT8104:我是MT8104回执报文喔

    由于我们的目的是消除if-else,那么这里需要将ReceiptHandleStrategyFactory策略工厂进行改造下,采用字典的方式存放我的策略,而Map具备key-value结构,采用Map是个不错选择。

    稍微改造下,代码如下

    经过对策略模式+简单工厂方案的改造,我们已经消除了if-else的结构,每当新来了一种回执,只需要添加新的回执处理策略,并修改ReceiptHandleStrategyFactory中的Map集合。

    如果要使得程序符合开闭原则,则需要调整ReceiptHandleStrategyFactory中处理策略的获取方式,通过反射的方式,获取指定包下的所有IReceiptHandleStrategy实现类,然后放到字典Map中去。

    责任链模式

    责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。

    发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任

    回执处理者接口

    责任链接口

    责任链接口实现类

    具体回执处理者

    责任链处理者容器(如果采用spring,则可以通过依赖注入的方式获取到IReceiptHandler的子类对象)

    客户端

    解析报文MT2101:我是MT2101回执报文喔

    解析报文MT8104:我是MT8104回执报文喔

    通过责任链的处理方式,if-else结构也被我们消除了,每当新来了一种回执,只需要添加IReceiptHandler实现类并修改ReceiptHandlerContainer处理者容器即可,如果要使得程序符合开闭原则,则需要调整ReceiptHandlerContainer中处理者的获取方式,通过反射的方式,获取指定包下的所有IReceiptHandler实现类。

    这里使用到了一个反射工具类,用于获取指定接口的所有实现类

    接下来改造ReceiptHandlerContainer

    至此,该方案完美符合了开闭原则,如果新增一个回执类型,只需要添加一个新的回执处理器即可,无需做其它改动。如新加了MT6666的回执,代码如下

    策略模式+注解

    此方案其实和上述没有太大异同,为了能符合开闭原则,通过自定义注解的方式,标记处理者类,然后反射获取到该类集合,放到Map容器中,这里不再赘述

    小结

    if-else或switch case 这种分支判断的方式对于分支逻辑不多的简单业务,还是直观高效的。对于业务复杂,分支逻辑多,采用适当的模式技巧,会让代码更加清晰,容易维护,但同时类或方法数量也是倍增的。我们需要对业务做好充分分析,避免一上来就设计模式,避免过度设计!

    推荐阅读:

    牛皮了,马士兵老师全网首播阿里P8级技术、实现大型淘宝实战落

    面试美团被JVM惨虐?阿里P9架构师用500分钟把JVM从入门讲到实战#合集

    清华启蒙架构师马士兵针对应届生到开发十年的Java程序员做职业把脉

    马士兵教育:Spring源码实战全集,资深架构师带你搞懂Spring源码底层从入门到入坟

    阿里P9架构师120分钟带你掌握线程池,不在为线程而烦恼

    相关文章

      网友评论

        本文标题:太牛了,竟然用设计模式来干掉 if-else

        本文链接:https://www.haomeiwen.com/subject/zlqrqktx.html