前言

物流行业中,通常会涉及到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 {

    @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<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();
} @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<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() {
@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<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 {

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

用设计模式来替代if-else的更多相关文章

  1. 设计模式来替代if-else

    前言# 物流行业中,通常会涉及到EDI报文(XML格式文件)传输和回执接收,每发送一份EDI报文,后续都会收到与之关联的回执(标识该数据在第三方系统中的流转状态).这里枚举几种回执类型:MT1101. ...

  2. “五年经验”年薪50W分享Java程序员掌握什么技术才不会被淘汰

    在这个IT系统动辄就是上亿流量的时代,Java作为大数据时代应用最广泛的语言,诞生了一批又一批的新技术,包括HBase.Hadoop.MQ.Netty.SpringCloud等等 . 一些独角兽公司以 ...

  3. 同样是搞Java,年薪15W和50W到底差在哪里?

    同样是搞Java,年薪15W和50W到底差在哪里? 一.总结 一句话总结: 学习 挑战 1.扩宽自己的眼界,学着从全局看待问题,并且勇于挑战别人眼中的难题 2.持续提升你的学习能力,虽然有很多人以「在 ...

  4. 《设计模式面试小炒》策略和工厂模式替代业务场景中复杂的ifelse

    <设计模式面试小炒>策略和工厂模式替代业务场景中复杂的ifelse 我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟! 囧囧表示:小白面试最怕的就是面试官问的知识点太笼统, ...

  5. C#设计模式

    自从上次记录完“重构之道”以后,查询设计模式挺麻烦的.就打算把原先写的设计模式系列合并一下. 设计原则 使用设计模式的根本原因就是适应需求变化,提高代码的复用率,使程序更具有扩展性和可维护性. SOL ...

  6. C++ 系列:设计模式研究

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  7. HTML设计模式学习笔记

    本周我主要学习了HTML的设计模式,现将我的学习内容总结如下: 一.盒模型的学习 CSS中有一种基础的设计模型叫做盒模型,它定义了元素是如何被看做盒子来解析的.我主要学习了六种盒模型,分别为内联盒模型 ...

  8. 设计模式之里氏代换原则(LSP)

    里氏代换原则(Liskov Substitution Principle, LSP) 1 什么是里氏代换原则 里氏代换原则是由麻省理工学院(MIT)计算机科学实验室的Liskov女士,在1987年的O ...

  9. 谈谈JAVA工程狮面试中经常遇到的面试题目------什么是MVC设计模式

    作为一名java工程狮,大家肯定经历过很多面试,但每次几乎都会被问到什么是MVC设计模式,你是怎么理解MVC的类似这样的一系列关于MVC的问题. [出现频率] [关键考点] MVC的含义 MVC的结构 ...

随机推荐

  1. react-native start error Invalid regular expression:

    详细错误: error Invalid regular expression: /(.*\\__fixtures__\\.*|node_modules[\\\]react[\\\]dist[\\\]. ...

  2. powershell下载网站图片

    $picurl = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=10" $data = ...

  3. ipad已停用 连接itunes怎么办

    问题描述: ipad 开机密码多次输入出错后,提示 ipad已停用 连接itunes 解决方法: 参考: https://jingyan.baidu.com/article/fb48e8bee9ef4 ...

  4. (已实践)PLSQL本地还原Oracle数据库dmp文件

    这个方法很烂,导致重装Oracle时候处处出现问题,不建议使用这个方法,除非你以后不再用Oracle这个软件了,这个方法很烂,再评论一下. 第一,启动服务,(如果数据库处于启动状态,那么略过这一步) ...

  5. 命令行模式和Python交互模式的区别

    1.命令行模式: 在Windows开始菜单选择“命令提示符”,就进入到命令行模式,它的提示符类似C:\Users\>: 2.python交互模式 在命令行模式下敲命令python,就看到类似如下 ...

  6. C函数调用过程原理及函数栈帧分析(转)

    在x86的计算机系统中,内存空间中的栈主要用于保存函数的参数,返回值,返回地址,本地变量等.一切的函数调用都要将不同的数据.地址压入或者弹出栈.因此,为了更好地理解函数的调用,我们需要先来看看栈是怎么 ...

  7. 移动端HTML5开发问题汇总-样式篇

    问题:Android 上圆形图片使用 border 时,边框显示变形 解决:给 img 外嵌套一个元素,为其使用圆角 <div> <img src=""> ...

  8. MySQL-快速入门(10)触发器

    1.什么是触发器 触发器是与表有关的命名数据库对象.触发器由事件来触发某个操作. 触发器是特殊的存储过程,触发器不需要call语句来调用,也不需要手工启动,只需要确定一个预定义的事件发生的时候,就会被 ...

  9. uboot启动第一阶段分析

    一. uboot第一阶段初识 1.1. 什么是uboot第一阶段 1.1.1. 启动os三个阶段 1.1.1.1. bl0阶段 a. 这段代码是三星固化到iROM中,可以查看<S5PV210_i ...

  10. PC端微信防撤回功能分析

    1.打开PC端微信的安装目录,有一个WeChatWin.dll文件,微信的所有功能基本上都在这个文件中了 2.OD打开,搜索字符串revokemsg(撤回消息,掌握一门外语是多么的重要啊!!!),在所 ...