java干掉 if-else

news/2024/10/17 22:17:45/

前言 传统做法-if-else分支 策略模式+Map字典 责任链模式 策略模式+注解 物流行业中,通常会涉及到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分支,违反了开闭原则(对扩展开放,对修改关闭)

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

在上述场景中,我们可以把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中去。

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

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

回执处理者接口

/*** @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实现类。

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

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


http://www.ppmy.cn/news/1158582.html

相关文章

vue3后台管理框架之技术栈

vue3全家桶技术 基础构建&#xff1a; vue3vite4TypeScript 代码格式 &#xff1a; eslintprettystylelint git生命周期钩子&#xff1a; husky css预处理器&#xff1a; sass ui库&#xff1a; element-plus 模拟数据: mock 网络请求&#xff1a; axios 路由&#xff1a; vue…

亚马逊、eBay,速卖通,国际站买家账号支付异常问题解决方法

如何解决下单被砍、封号问题&#xff0c;建议采取以下措施&#xff1a; 买家账号下单&#xff0c;不单纯只是解决支付卡、IP问题就可以了&#xff0c;因为平台大数据风控点很多&#xff0c; 我们防关联具体要解决几个问题 一&#xff1a;要硬件参数的关联、安全码、地区码、…

TigerVNC自动设置密码脚本

应用场景&#xff1a;在大规模部署时&#xff0c;需要一个无需人工输入初始化密码的方式启动vnc。 新建sh脚本&#xff0c;复制以下命令。设置ssh终端密码和vncserver密码后&#xff0c;运行ssh脚本&#xff0c;即可设置修改vncserver密码。 #!/bin/bash vncpwd"vncserv…

深入理解Scrapy

Scrapy是什么 An open source and collaborative framework for extracting the data you need from websites. In a fast, simple, yet extensible way. Scrapy是适用于Python的一个快速、简单、功能强大的web爬虫框架&#xff0c;通常用于抓取web站点并从页面中提取结构化的数…

(vue)el-descriptions 描述列表无效

(vue)el-descriptions 描述列表无效 原因&#xff1a;element 的版本不够 解决&#xff1a;运行下面两个命令 npm uninstall element-ui //卸载之前安装的版本 npm i element-ui -S //重新安装解决参考&#xff1a;https://blog.csdn.net/weixin_59769148/article/details/1…

【机器学习】PyTorch-MNIST-手写字识别

文章目录 前言完成效果一、下载数据集手动下载代码下载MNIST数据集&#xff1a; 二、 展示图片三、DataLoader数据加载器四、搭建神经网络五、 训练和测试第一次运行&#xff1a; 六、优化模型第二次优化后运行&#xff1a; 七、完整代码八、手写板实现输入识别功能 前言 注意…

idea生成的hashCode方法里为什么选择数字31作为生成hashCode值的乘数

个人理解 原因一&#xff1a;更少的乘积结果冲突 31是质子数中一个“不大不小”的存在&#xff0c;如果你使用的是一个如2的较小质数&#xff0c;那么得出的乘积会在一个很小的范围&#xff0c;很容易造成哈希值的冲突。而如果选择一个100以上的质数&#xff0c;得出的哈希值会…

【数据结构C/C++】优先(级)队列

文章目录 什么是优先队列&#xff1f;堆排序代码实现408考研各数据结构C/C代码&#xff08;Continually updating&#xff09; 什么是优先队列&#xff1f; 下面的内容来自于百度百科。 如果我们给每个元素都分配一个数字来标记其优先级&#xff0c;不妨设较小的数字具有较高的…