前言
物流行业中,通常会涉及到EDI报文(XML格式文件)传输和回执接收,每发送一份EDI报文,后续都会收到与之关联的回执(标识该数据在第三方系统中的流转状态)。
这里枚举几种回执类型:MT1101、MT2101、MT4101、MT8104、MT8105、MT9999,系统在收到不同的回执报文后,会执行对应的业务逻辑处理。当然,实际业务场景并没有那么笼统,这里以回执处理为演示案例
模拟一个回执类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data public class Receipt {
String message;
String type;
}
|
模拟一个回执生成器
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 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("推送邮件"); } }
|
在遇到if-else的分支业务逻辑比较复杂时,我们都习惯于将其抽出一个方法或者封装成一个对象去调用,这样整个if-else结构就不会显得太臃肿。
就上面例子,当回执的类型越来越多时,分支else if 就会越来越多,每增加一个回执类型,就需要修改或添加if-else分支,违反了开闭原则(对扩展开放,对修改关闭)
策略模式+Map字典
我们知道, 策略模式的目的是封装一系列的算法,它们具有共性,可以相互替换,也就是说让算法独立于使用它的客户端而独立变化,客户端仅仅依赖于策略接口 。
在上述场景中,我们可以把if-else分支的业务逻辑抽取为各种策略,但是不可避免的是依然需要客户端写一些if-else进行策略选择的逻辑,我们可以将这段逻辑抽取到工厂类中去,这就是策略模式+简单工厂,代码如下
策略接口
1 2 3 4 5 6 7 8 9
|
public interface IReceiptHandleStrategy {
void handleReceipt(Receipt receipt);
}
|
策略接口实现类,也就是具体的处理者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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()); }
}
|
策略上下文类(策略接口的持有者)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class ReceiptStrategyContext {
private IReceiptHandleStrategy receiptHandleStrategy;
public void setReceiptHandleStrategy(IReceiptHandleStrategy receiptHandleStrategy) { this.receiptHandleStrategy = receiptHandleStrategy; }
public void handleReceipt(Receipt receipt){ if (receiptHandleStrategy != null) { receiptHandleStrategy.handleReceipt(receipt); } } }
|
策略工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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; } }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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是个不错选择。
稍微改造下,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
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中去。系统学习设计模式:设计模式内容聚合
责任链模式
责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任
回执处理者接口
1 2 3 4 5 6 7 8 9
|
public interface IReceiptHandler {
void handleReceipt(Receipt receipt,IReceiptHandleChain handleChain);
}
|
责任链接口
1 2 3 4 5 6 7 8
|
public interface IReceiptHandleChain {
void handleReceipt(Receipt receipt); }
|
责任链接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
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); } } } }
|
具体回执处理者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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的子类对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
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; }
}
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12
| 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知音公众号内回复“后端面试”,送你一份面试宝典
这里使用到了一个反射工具类,用于获取指定接口的所有实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
public class ReflectionUtil {
private static final Set<Class<?>> CLASS_SET;
static { CLASS_SET = getClassSet("com.yaolong"); }
public static ClassLoader getClassLoader(){ return Thread.currentThread().getContextClassLoader(); }
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); }
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; }
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; }
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ReceiptHandlerContainer {
private ReceiptHandlerContainer(){}
public static List<IReceiptHandler> getReceiptHandlerList(){ List<IReceiptHandler> receiptHandlerList = new ArrayList<>(); 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的回执,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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 这种分支判断的方式对于分支逻辑不多的简单业务,还是直观高效的。对于业务复杂,分支逻辑多,采用适当的模式技巧,会让代码更加清晰,容易维护,但同时类或方法数量也是倍增的。我们需要对业务做好充分分析,避免一上来就设计模式,避免过度设计!