点击关注公众号,利用碎片时间学习

前言

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

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

模拟一个回执类

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

模拟一个回执生成器

public class ReceiptBuilder {public static List<Receipt> generateReceiptList(){//直接模拟一堆回执对象List<Receipt> 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<Receipt> 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 {@Overridepublic void handleReceipt(Receipt receipt) {System.out.println("解析报文MT2101:" + receipt.getMessage());}}public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy {@Overridepublic void handleReceipt(Receipt receipt) {System.out.println("解析报文MT1101:" + receipt.getMessage());}}public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy {@Overridepublic void handleReceipt(Receipt receipt) {System.out.println("解析报文MT8104:" + receipt.getMessage());}}public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy {@Overridepublic 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<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是个不错选择。稍微改造下,代码如下

/*** @Description: 策略工厂* @Auther: wuzhazha*/
public class ReceiptHandleStrategyFactory {private static Map<String,IReceiptHandleStrategy> 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中去。学习资料:Java进阶视频资源

责任链模式

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任

回执处理者接口

/*** @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<IReceiptHandler> receiptHandlerList;static {//从容器中获取处理器对象receiptHandlerList = ReceiptHandlerContainer.getReceiptHandlerList();}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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<IReceiptHandler> getReceiptHandlerList(){List<IReceiptHandler> receiptHandlerList = new ArrayList<>();receiptHandlerList.add(new Mt2101ReceiptHandler());receiptHandlerList.add(new Mt8104ReceiptHandler());return receiptHandlerList;}}

客户端

public class Client {public static void main(String[] args) {//模拟回执List<Receipt> 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<?>> 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<Class<?>> getClassSet(String packageName) {Set<Class<?>> classSet = new HashSet<>();try {Enumeration<URL> 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<JarEntry> 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<Class<?>> classSet, String className) {Class<?> cls = loadClass(className,false);classSet.add(cls);}private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {final File[] files = new File(packagePath).listFiles(new FileFilter() {@Overridepublic 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<Class<?>> getClassSet() {return CLASS_SET;}/*** 获取应用包名下某父类(或接口)的所有子类(或实现类)* @param superClass* @return*/public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) {Set<Class<?>> 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<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) {Set<Class<?>> 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<IReceiptHandler> getReceiptHandlerList(){List<IReceiptHandler> receiptHandlerList = new ArrayList<>();//获取IReceiptHandler接口的实现类Set<Class<?>> 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 {@Overridepublic 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 这种分支判断的方式对于分支逻辑不多的简单业务,还是直观高效的。对于业务复杂,分支逻辑多,采用适当的模式技巧,会让代码更加清晰,容易维护,但同时类或方法数量也是倍增的。我们需要对业务做好充分分析,避免一上来就设计模式,避免过度设计!

来源:cnblogs.com/DiDi516/p/11787257.html

推荐:

主流Java进阶技术(学习资料分享)

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

如何巧用设计模式,干掉if-else相关推荐

  1. 三句话巧记 23 种设计模式

    大家都知道 23 种设计模式,其中又可以分成三类,创建型模式,结构型模式,行为型模式.但是总是在实际应用中忘记了,当具体看到一些代码的时候也想不起来具体对应的是哪种设计模式,对经常重构的代码人员来说是 ...

  2. 来吧,用设计模式来干掉 if-else

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:DiDi516 cnblogs.com/DiDi516/p/ ...

  3. JavaScript 设计模式 : 巧用'工厂模式'和'创建者'模式

    我为什么把他们两个放在一起讲?我觉得这两个设计模式有相似之处,有时候会一个设计模式不能满足你的需求而采用另一种设计模式.基于这点考虑,而且为了大家更好地理解,我放到了一起,加深大家的印象,活学活用. ...

  4. 巧用代理设计模式(Proxy Design Pattern)改善前端图片加载体验

    这篇文章介绍一种使用代理设计模式(Proxy Design Pattern)的方法来改善您的前端应用里图片加载的体验. 假设我们的应用里需要显示一张尺寸很大的图片,位于远端服务器.我们用一些前端框架的 ...

  5. 干掉if else!试试这三种设计模式,优化代码贼顺手!

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

  6. 设计模式在工作中的实践

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:https://www.cnblogs.com/ch ...

  7. Spring是如何运用设计模式的?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:爱宝贝 oschina.net/zhangxufeng/bl ...

  8. 设计模式在我工作中的巧妙实践

    设计模式在我工作中的实践 大多数时候我都是写一些业务代码,可能一堆 CRUD 就能解决问题,但是这样的工作对技术人的提升并不多,如何让自己从业务中解脱出来找到写代码的乐趣呢,我做过一些尝试,使用设计模 ...

  9. SpringBoot如何使用策略模式干掉if else

    场景:当我们接收到一些数据需要对其进行处理时,由于它们来自于不同的渠道(如:腾讯,头条),不同渠道所需的处理方式不同,下面我们写一个简单Demo来实现该的场景. 解决思路 实例一:简单举例 1.首先构 ...

最新文章

  1. javascript 操作Word和Excel的实现代码
  2. 使用kubectl delete pods xxx删除对应的pod,提示删除成功,但是立马又会生成一个。为何?
  3. MySQL5添加外键约束错误 (Error Code : 1005)
  4. 《社交网站界面设计(原书第2版)》——1.12 传感器在交互中的作用
  5. a59s刷机包卡刷 oppo_OPPO A59s刷机教程_OPPO A59s升级更新官方系统包
  6. 当前服务器更新维护公告,【已开服】1月17日全部服务器更新维护公告
  7. linux命令中ll和ls的区别
  8. 塑料填充母粒行业调研报告 - 市场现状分析与发展前景预测
  9. RIGHT-BICEP测试第二次
  10. 炉石无限重新连接服务器,炉石传说:竟然还有这种无限资源的操作
  11. 苹果CMS个人收款扫码收款插件 闪电收款
  12. OpenGL ES 模拟器
  13. 魔法币 java_网易2018校招笔试编程题-魔法币 java实现
  14. 微信小程序自定义tabBar(实操)
  15. LeetCode题解汇总
  16. 无线服务器功能,索尼WG-C20无线服务器 功能介绍
  17. 《数字电子技术课程设计》课程笔记(五)————调试总结
  18. houdini 程序化 序
  19. java pdf转txt【完整代码包含jar包】
  20. summernote java_summernote编辑器插件使用笔记

热门文章

  1. c语言基础循环程序设计,C语言编程新手入门基础——循环详解!
  2. jmeter 登录(录制 )
  3. 变阻抗控制理论基础、公式推导
  4. 短截线匹配matlab计算输入输出阻抗
  5. 什么是REITs基金?
  6. 使用Python自带的IDLE默认打开.py文件
  7. Linux内存从0到1学习笔记(9.1,内存优化调试之kswapd0触发原理)
  8. 如何修改Linux的远程端口
  9. 【文献笔记】《使用深度卷积神经网络预估剩余有用寿命》
  10. CSS基础面试题精选