移除 if-else

移除 if-else

前言

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

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

模拟一个回执类

@Datapublic class Receipt { /** * 回执信息 */ String message; /** * 回执类型(`MT1101、MT2101、MT4101、MT8104、MT8105、MT9999`) */ String type;}

模拟一个回执生成器

public class ReceiptBuilder { public static List generateReceiptList(){ //直接模拟一堆回执对象 List receiptList = new ArrayList(); receiptList.add(new Receipt(“我是MT2101回执喔”,”MT2101″)); receiptList.add(new Receipt(“我是MT1101回执喔”,”MT1101″)); receiptList.add(new Receipt(“我是MT8104回执喔”,”MT8104″)); receiptList.add(new Receipt(“我是MT9999回执喔”,”MT9999″)); //…… return receiptList; }}

复制

传统做法-if-else分支

List receiptList = ReceiptBuilder.generateReceiptList();//循环处理for (Receipt receipt : receiptList) { if (StringUtils.equals(“MT2101”,receipt.getType())) { System.out.println(“接收到MT2101回执”); System.out.println(“解析回执内容”); System.out.println(“执行业务逻辑”); } else if (StringUtils.equals(“MT1101”,receipt.getType())) { System.out.println(“接收到MT1101回执”); System.out.println(“解析回执内容”); System.out.println(“执行业务逻辑”); } else if (StringUtils.equals(“MT8104”,receipt.getType())) { System.out.println(“接收到MT8104回执”); System.out.println(“解析回执内容”); System.out.println(“执行业务逻辑”); } else if (StringUtils.equals(“MT9999”,receipt.getType())) { System.out.println(“接收到MT9999回执”); System.out.println(“解析回执内容”); System.out.println(“执行业务逻辑”); System.out.println(“推送邮件”); } // ……未来可能还有好多个else if}

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

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

策略模式+Map字典

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

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

策略接口

/** * @Description: 回执处理策略接口 * @Auther: wuzhazha */public interface IReceiptHandleStrategy { void handleReceipt(Receipt receipt);}

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

public class Mt2101ReceiptHandleStrategy implements IReceiptHandleStrategy { @Override public void handleReceipt(Receipt receipt) { System.out.println(“解析报文MT2101:” + receipt.getMessage()); }}public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy { @Override public void handleReceipt(Receipt receipt) { System.out.println(“解析报文MT1101:” + receipt.getMessage()); }}public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy { @Override public void handleReceipt(Receipt receipt) { System.out.println(“解析报文MT8104:” + receipt.getMessage()); }}public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy { @Override public void handleReceipt(Receipt receipt) { System.out.println(“解析报文MT9999:” + receipt.getMessage()); }}

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

/** * @Description: 上下文类,持有策略接口 * @Auther: wuzhazha */public class ReceiptStrategyContext { private IReceiptHandleStrategy receiptHandleStrategy; /** * 设置策略接口 * @param receiptHandleStrategy */ public void setReceiptHandleStrategy(IReceiptHandleStrategy receiptHandleStrategy) { this.receiptHandleStrategy = receiptHandleStrategy; } public void handleReceipt(Receipt receipt){ if (receiptHandleStrategy != null) { receiptHandleStrategy.handleReceipt(receipt); } }}

策略工厂

/** * @Description: 策略工厂 * @Auther: wuzhazha */public class ReceiptHandleStrategyFactory { private ReceiptHandleStrategyFactory(){} public static IReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){ IReceiptHandleStrategy receiptHandleStrategy = null; if (StringUtils.equals(“MT2101”,receiptType)) { receiptHandleStrategy = new Mt2101ReceiptHandleStrategy(); } else if (StringUtils.equals(“MT8104”,receiptType)) { receiptHandleStrategy = new Mt8104ReceiptHandleStrategy(); } return receiptHandleStrategy; }}

客户端

public class Client { public static void main(String[] args) { //模拟回执 List 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是个不错选择。

稍微改造下,代码如下

/** * @Description: 策略工厂 * @Auther: wuzhazha */public class ReceiptHandleStrategyFactory { private static Map receiptHandleStrategyMap; private ReceiptHandleStrategyFactory(){ this.receiptHandleStrategyMap = new HashMap(); this.receiptHandleStrategyMap.put(“MT2101”,new Mt2101ReceiptHandleStrategy()); this.receiptHandleStrategyMap.put(“MT8104”,new Mt8104ReceiptHandleStrategy()); } public static IReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){ return receiptHandleStrategyMap.get(receiptType); }}

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

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

责任链模式

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

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

回执处理者接口

/** * @Description: 抽象回执处理者接口 * @Auther: wuzhazha */public interface IReceiptHandler { void handleReceipt(Receipt receipt,IReceiptHandleChain handleChain);}

责任链接口

/** * @Description: 责任链接口 * @Auther: wuzhazha */public interface IReceiptHandleChain { void handleReceipt(Receipt receipt);}

责任链接口实现类

/** * @Description: 责任链实现类 * @Auther: wuzhazha */public class ReceiptHandleChain implements IReceiptHandleChain { //记录当前处理者位置 private int index = 0; //处理者集合 private static List receiptHandlerList; static { //从容器中获取处理器对象 receiptHandlerList = ReceiptHandlerContainer.getReceiptHandlerList(); } @Override public void handleReceipt(Receipt receipt) { if (receiptHandlerList !=null && receiptHandlerList.size() > 0) { if (index != receiptHandlerList.size()) { IReceiptHandler receiptHandler = receiptHandlerList.get(index++); receiptHandler.handleReceipt(receipt,this); } } }}

具体回执处理者

public class Mt2101ReceiptHandler implements IReceiptHandler { @Override public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) { if (StringUtils.equals(“MT2101”,receipt.getType())) { System.out.println(“解析报文MT2101:” + receipt.getMessage()); } //处理不了该回执就往下传递 else { handleChain.handleReceipt(receipt); } }}public class Mt8104ReceiptHandler implements IReceiptHandler { @Override public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) { if (StringUtils.equals(“MT8104”,receipt.getType())) { System.out.println(“解析报文MT8104:” + receipt.getMessage()); } //处理不了该回执就往下传递 else { handleChain.handleReceipt(receipt); } }}

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

/** * @Description: 处理者容器 * @Auther: wuzhazha */public class ReceiptHandlerContainer { private ReceiptHandlerContainer(){} public static List getReceiptHandlerList(){ List receiptHandlerList = new ArrayList(); receiptHandlerList.add(new Mt2101ReceiptHandler()); receiptHandlerList.add(new Mt8104ReceiptHandler()); return receiptHandlerList; }}

客户端

public class Client { public static void main(String[] args) { //模拟回执 List receiptList = ReceiptBuilder.generateReceiptList(); for (Receipt receipt : receiptList) { //回执处理链对象 ReceiptHandleChain receiptHandleChain = new ReceiptHandleChain(); receiptHandleChain.handleReceipt(receipt); } }}

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

通过责任链的处理方式,if-else结构也被我们消除了,每当新来了一种回执,只需要添加IReceiptHandler实现类并修改ReceiptHandlerContainer处理者容器即可,如果要使得程序符合开闭原则,则需要调整ReceiptHandlerContainer中处理者的获取方式,通过反射的方式,获取指定包下的所有IReceiptHandler实现类。Java知音公众号内回复“后端面试”,送你一份面试宝典

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

/** * @Description: 反射工具类 * @Auther: wuzhazha */public class ReflectionUtil { /** * 定义类集合(用于存放所有加载的类) */ private static final Set CLASS_SET; static { //指定加载包路径 CLASS_SET = getClassSet(“com.yaolong”); } /** * 获取类加载器 * @return */ public static ClassLoader getClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * 加载类 * @param className 类全限定名称 * @param isInitialized 是否在加载完成后执行静态代码块 * @return */ public static Class loadClass(String className,boolean isInitialized) { Class cls; try { cls = Class.forName(className,isInitialized,getClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return cls; } public static Class loadClass(String className) { return loadClass(className,true); } /** * 获取指定包下所有类 * @param packageName * @return */ public static Set getClassSet(String packageName) { Set classSet = new HashSet(); try { Enumeration urls = getClassLoader().getResources(packageName.replace(“.”,”/”)); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals(“file”)) { String packagePath = url.getPath().replace(“%20″,””); addClass(classSet,packagePath,packageName); } else if (protocol.equals(“jar”)) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); if (jarURLConnection != null) { JarFile jarFile = jarURLConnection.getJarFile(); if (jarFile != null) { Enumeration jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(“.class”)) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(“.”)).replaceAll(“/”, “.”); doAddClass(classSet,className); } } } } } } } } catch (IOException e) { throw new RuntimeException(e); } return classSet; } private static void doAddClass(Set classSet, String className) { Class cls = loadClass(className,false); classSet.add(cls); } private static void addClass(Set classSet, String packagePath, String packageName) { final File[] files = new File(packagePath).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(“.class”)) || file.isDirectory(); } }); for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = fileName.substring(0, fileName.lastIndexOf(“.”)); if (StringUtils.isNotEmpty(packageName)) { className = packageName + “.” + className; } doAddClass(classSet,className); } else { String subPackagePath = fileName; if (StringUtils.isNotEmpty(packagePath)) { subPackagePath = packagePath + “/” + subPackagePath; } String subPackageName = fileName; if (StringUtils.isNotEmpty(packageName)) { subPackageName = packageName + “.” + subPackageName; } addClass(classSet,subPackagePath,subPackageName); } } } public static Set getClassSet() { return CLASS_SET; } /** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */ public static Set getClassSetBySuper(Class superClass) { Set classSet = new HashSet(); for (Class cls : CLASS_SET) { if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) { classSet.add(cls); } } return classSet; } /** * 获取应用包名下带有某注解的类 * @param annotationClass * @return */ public static Set getClassSetByAnnotation(Class annotationClass) { Set classSet = new HashSet(); for (Class cls : CLASS_SET) { if (cls.isAnnotationPresent(annotationClass)) { classSet.add(cls); } } return classSet; }}

接下来改造ReceiptHandlerContainer

public class ReceiptHandlerContainer { private ReceiptHandlerContainer(){} public static List getReceiptHandlerList(){ List receiptHandlerList = new ArrayList(); //获取IReceiptHandler接口的实现类 Set classList = ReflectionUtil.getClassSetBySuper(IReceiptHandler.class); if (classList != null && classList.size() > 0) { for (Class clazz : classList) { try { receiptHandlerList.add((IReceiptHandler)clazz.newInstance()); } catch ( Exception e) { e.printStackTrace(); } } } return receiptHandlerList; }}

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

public class Mt6666ReceiptHandler implements IReceiptHandler { @Override public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) { if (StringUtils.equals(“MT6666”,receipt.getType())) { System.out.println(“解析报文MT6666:” + receipt.getMessage()); } //处理不了该回执就往下传递 else { handleChain.handleReceipt(receipt); } }}

策略模式+注解

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

小结

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

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月21日
下一篇 2022年6月21日

相关推荐

  • iPhone信号差?用这几种方法解决

    iPhone这几代的机型都饱受非议,事实也确实如此,即使买的高配机型信号也不如国产机稳定,且常年信号3格2格。归结其原因,首先因为苹果公司致力于自主研发基带,不愿受其他厂商限制,采…

    2022年8月1日
  • 戴维斯,湖人女老板珍妮巴斯叫你来背锅!威少、詹姆斯、沃格尔呢

    洛杉矶湖人队上演的这部大戏已经来到高潮阶段,上赛季拉垮表现使得球队未能进入季后赛,现在产生的后续影响正在上演。众所周知,湖人上赛季重组阵容失败,引入威少被认为是湖人队最大的败笔,因…

    2022年7月21日
  • 两项ESG团体标准在京启动

    工人日报-中工网记者 杜鑫 7月24日,在由中国社会责任百人论坛ESG(环境、社会、公司治理)专委会、航空工业集团联合主办,中航产融、责任云研究院联合承办的“ESG中国论坛2022…

    2022年8月3日
  • 项目回顾:电商之预付项目

    先付后买 眨眼间,我跳槽到互联网公司两年有余,在这两年里,我带过各种不同的项目,从后端电商平台到前端场运营,从供应链到物流,从促销技术项目经理到全领域项目经理,这与之前从事的产品迭…

    2022年6月16日
  • IBM 开始辞退所有在俄员工

    他们自三月就已经停止所有业务,但却维持支薪。 IBM IBM CEO Arvind Krishna 在最新的备忘录宣布,今天起正式结束所有在俄营运。据报导,虽然IBM 是其中一家在…

    2022年6月12日
  • 滴滴再次被约谈!这次是因为它的打车小程序…

    据交通运输部近日报道,交通运输新业态协同监管部际联席会议办公室对滴滴出行、T3出行、高德、曹操出行、首汽约车、美团打车、万顺叫车、享道出行、如祺出行、阳光出行、嘀嗒出行等11家平台…

    2022年8月24日
  • 召唤与合成打折推荐|召唤与合成手游是一款网络角色放置合成游戏

    召唤与合成是一款经典的网络角色放置合成游戏,二次元卡牌游戏。它是一个游戏内容丰富的游戏,每个玩家都可以在精力充沛的骑士的帮助下,逐渐变强,不断战胜面前的敌人,不断刻下冒险的烙印,成…

    2022年8月10日
  • 贪小利 毁大局

    来源:【陕西农村报(陕西农村网)】 暑假,出行者众多。近日,有游客爆料称,在购买景区中巴车票时,遭遇了被默认搭售保险的情况。该游客在个人社交平台上表示,疫情防控背景下,景区运营不容…

    2022年8月12日
  • 中国人寿能买摩托车交强险吗 教你如何购买!

    根据有关的规定,摩托车和汽车是一样的,每年都需要交交强险,而有的小伙伴不知道在哪里可以交费,那么中国人寿能买摩托车交强险吗?下文就来带大家了解一下。 中国人寿是不能买托车交强险的,…

    2022年9月30日
  • 许多事情已经改变|2022最受创业者欢迎投资人/投资机构名册发布

    调研 黄祝熹、郭允骁、周偌伊、毛景宣 编辑 刘旌 出品 36氪创投研究院 在才过去不久的「X·36under36创业者盛典」上,许知远向两名人工智能公司创始人发问:“我对新一代创业…

    2022年6月28日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息