前言
物流行業(yè)中,通常會(huì)涉及到EDI報(bào)文(XML格式文件)傳輸和回執(zhí)接收,每發(fā)送一份EDI報(bào)文,后續(xù)都會(huì)收到與之關(guān)聯(lián)的回執(zhí)(標(biāo)識(shí)該數(shù)據(jù)在第三方系統(tǒng)中的流轉(zhuǎn)狀態(tài))。
這里枚舉幾種回執(zhí)類型:MT1101、MT2101、MT4101、MT8104、MT8105、MT9999,系統(tǒng)在收到不同的回執(zhí)報(bào)文后,會(huì)執(zhí)行對(duì)應(yīng)的業(yè)務(wù)邏輯處理。當(dāng)然,實(shí)際業(yè)務(wù)場(chǎng)景并沒(méi)有那么籠統(tǒng),這里以回執(zhí)處理為演示案例。
模擬一個(gè)回執(zhí)類
@Data
public class Receipt {
/**
* 回執(zhí)信息
*/
String message;
/**
* 回執(zhí)類型(`MT1101、MT2101、MT4101、MT8104、MT8105、MT9999`)
*/
String type;
}
模擬一個(gè)回執(zhí)生成器
public class ReceiptBuilder {
public static List<Receipt> generateReceiptList(){
//直接模擬一堆回執(zhí)對(duì)象
List<Receipt> receiptList = new ArrayList<>();
receiptList.add(new Receipt("我是MT2101回執(zhí)喔","MT2101"));
receiptList.add(new Receipt("我是MT1101回執(zhí)喔","MT1101"));
receiptList.add(new Receipt("我是MT8104回執(zhí)喔","MT8104"));
receiptList.add(new Receipt("我是MT9999回執(zhí)喔","MT9999"));
//......
return receiptList;
}
}
傳統(tǒng)做法-if-else分支
List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();
//循環(huán)處理
for (Receipt receipt : receiptList) {
if (StringUtils.equals("MT2101",receipt.getType())) {
System.out.println("接收到MT2101回執(zhí)");
System.out.println("解析回執(zhí)內(nèi)容");
System.out.println("執(zhí)行業(yè)務(wù)邏輯");
} else if (StringUtils.equals("MT1101",receipt.getType())) {
System.out.println("接收到MT1101回執(zhí)");
System.out.println("解析回執(zhí)內(nèi)容");
System.out.println("執(zhí)行業(yè)務(wù)邏輯");
} else if (StringUtils.equals("MT8104",receipt.getType())) {
System.out.println("接收到MT8104回執(zhí)");
System.out.println("解析回執(zhí)內(nèi)容");
System.out.println("執(zhí)行業(yè)務(wù)邏輯");
} else if (StringUtils.equals("MT9999",receipt.getType())) {
System.out.println("接收到MT9999回執(zhí)");
System.out.println("解析回執(zhí)內(nèi)容");
System.out.println("執(zhí)行業(yè)務(wù)邏輯");
System.out.println("推送郵件");
}
// ......未來(lái)可能還有好多個(gè)else if
}
在遇到if-else的分支業(yè)務(wù)邏輯比較復(fù)雜時(shí),我們都習(xí)慣于將其抽出一個(gè)方法或者封裝成一個(gè)對(duì)象去調(diào)用,這樣整個(gè)if-else結(jié)構(gòu)就不會(huì)顯得太臃腫。
就上面例子,當(dāng)回執(zhí)的類型越來(lái)越多時(shí),分支else if 就會(huì)越來(lái)越多,每增加一個(gè)回執(zhí)類型,就需要修改或添加if-else分支,違反了開(kāi)閉原則(對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉)
策略模式+Map字典
我們知道, 策略模式的目的是封裝一系列的算法,它們具有共性,可以相互替換,也就是說(shuō)讓算法獨(dú)立于使用它的客戶端而獨(dú)立變化,客戶端僅僅依賴于策略接口 。
在上述場(chǎng)景中,我們可以把if-else分支的業(yè)務(wù)邏輯抽取為各種策略,但是不可避免的是依然需要客戶端寫一些if-else進(jìn)行策略選擇的邏輯,我們可以將這段邏輯抽取到工廠類中去,這就是策略模式+簡(jiǎn)單工廠,代碼如下
策略接口
/**
* @Description: 回執(zhí)處理策略接口
* @Auther: wuzhazha
*/
public interface IReceiptHandleStrategy {
void handleReceipt(Receipt receipt);
}
策略接口實(shí)現(xiàn)類,也就是具體的處理者
public class Mt2101ReceiptHandleStrategy implements IReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析報(bào)文MT2101:" + receipt.getMessage());
}
}
public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析報(bào)文MT1101:" + receipt.getMessage());
}
}
public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析報(bào)文MT8104:" + receipt.getMessage());
}
}
public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy {
@Override
public void handleReceipt(Receipt receipt) {
System.out.println("解析報(bào)文MT9999:" + receipt.getMessage());
}
}
策略上下文類(策略接口的持有者)
/**
* @Description: 上下文類,持有策略接口
* @Auther: wuzhazha
*/
public class ReceiptStrategyContext {
private IReceiptHandleStrategy receiptHandleStrategy;
/**
* 設(shè)置策略接口
* @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) {
//模擬回執(zhí)
List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();
//策略上下文
ReceiptStrategyContext receiptStrategyContext = new ReceiptStrategyContext();
for (Receipt receipt : receiptList) {
//獲取并設(shè)置策略
IReceiptHandleStrategy receiptHandleStrategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType());
receiptStrategyContext.setReceiptHandleStrategy(receiptHandleStrategy);
//執(zhí)行策略
receiptStrategyContext.handleReceipt(receipt);
}
}
}
解析報(bào)文MT2101:我是MT2101回執(zhí)報(bào)文喔 解析報(bào)文MT8104:我是MT8104回執(zhí)報(bào)文喔
由于我們的目的是消除if-else,那么這里需要將ReceiptHandleStrategyFactory策略工廠進(jìn)行改造下,采用字典的方式存放我的策略,而Map具備key-value結(jié)構(gòu),采用Map是個(gè)不錯(cuò)選擇。
稍微改造下,代碼如下
/**
* @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);
}
}
經(jīng)過(guò)對(duì)策略模式+簡(jiǎn)單工廠方案的改造,我們已經(jīng)消除了if-else的結(jié)構(gòu),每當(dāng)新來(lái)了一種回執(zhí),只需要添加新的回執(zhí)處理策略,并修改ReceiptHandleStrategyFactory中的Map集合。
如果要使得程序符合開(kāi)閉原則,則需要調(diào)整ReceiptHandleStrategyFactory中處理策略的獲取方式,通過(guò)反射的方式,獲取指定包下的所有IReceiptHandleStrategy實(shí)現(xiàn)類,然后放到字典Map中去。
責(zé)任鏈模式
責(zé)任鏈模式是一種對(duì)象的行為模式。在責(zé)任鏈模式里,很多對(duì)象由每一個(gè)對(duì)象對(duì)其下家的引用而連接起來(lái)形成一條鏈。請(qǐng)求在這個(gè)鏈上傳遞,直到鏈上的某一個(gè)對(duì)象決定處理此請(qǐng)求。
發(fā)出這個(gè)請(qǐng)求的客戶端并不知道鏈上的哪一個(gè)對(duì)象最終處理這個(gè)請(qǐng)求,這使得系統(tǒng)可以在不影響客戶端的情況下動(dòng)態(tài)地重新組織和分配責(zé)任
回執(zhí)處理者接口
/**
* @Description: 抽象回執(zhí)處理者接口
* @Auther: wuzhazha
*/
public interface IReceiptHandler {
void handleReceipt(Receipt receipt,IReceiptHandleChain handleChain);
}
責(zé)任鏈接口
/**
* @Description: 責(zé)任鏈接口
* @Auther: wuzhazha
*/
public interface IReceiptHandleChain {
void handleReceipt(Receipt receipt);
}
責(zé)任鏈接口實(shí)現(xiàn)類
/**
* @Description: 責(zé)任鏈實(shí)現(xiàn)類
* @Auther: wuzhazha
*/
public class ReceiptHandleChain implements IReceiptHandleChain {
//記錄當(dāng)前處理者位置
private int index = 0;
//處理者集合
private static List<IReceiptHandler> receiptHandlerList;
static {
//從容器中獲取處理器對(duì)象
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);
}
}
}
}
具體回執(zhí)處理者
public class Mt2101ReceiptHandler implements IReceiptHandler {
@Override
public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) {
if (StringUtils.equals("MT2101",receipt.getType())) {
System.out.println("解析報(bào)文MT2101:" + receipt.getMessage());
}
//處理不了該回執(zhí)就往下傳遞
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("解析報(bào)文MT8104:" + receipt.getMessage());
}
//處理不了該回執(zhí)就往下傳遞
else {
handleChain.handleReceipt(receipt);
}
}
}
責(zé)任鏈處理者容器(如果采用spring,則可以通過(guò)依賴注入的方式獲取到IReceiptHandler的子類對(duì)象)
/**
* @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) {
//模擬回執(zhí)
List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();
for (Receipt receipt : receiptList) {
//回執(zhí)處理鏈對(duì)象
ReceiptHandleChain receiptHandleChain = new ReceiptHandleChain();
receiptHandleChain.handleReceipt(receipt);
}
}
}
解析報(bào)文MT2101:我是MT2101回執(zhí)報(bào)文喔 解析報(bào)文MT8104:我是MT8104回執(zhí)報(bào)文喔
通過(guò)責(zé)任鏈的處理方式,if-else結(jié)構(gòu)也被我們消除了,每當(dāng)新來(lái)了一種回執(zhí),只需要添加IReceiptHandler實(shí)現(xiàn)類并修改ReceiptHandlerContainer處理者容器即可,如果要使得程序符合開(kāi)閉原則,則需要調(diào)整ReceiptHandlerContainer中處理者的獲取方式,通過(guò)反射的方式,獲取指定包下的所有IReceiptHandler實(shí)現(xiàn)類。
這里使用到了一個(gè)反射工具類,用于獲取指定接口的所有實(shí)現(xiàn)類
/**
* @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 是否在加載完成后執(zhí)行靜態(tài)代碼塊
* @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;
}
/**
* 獲取應(yīng)用包名下某父類(或接口)的所有子類(或?qū)崿F(xiàn)類)
* @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;
}
/**
* 獲取應(yīng)用包名下帶有某注解的類
* @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;
}
}
接下來(lái)改造ReceiptHandlerContainer
public class ReceiptHandlerContainer {
private ReceiptHandlerContainer(){}
public static List<IReceiptHandler> getReceiptHandlerList(){
List<IReceiptHandler> receiptHandlerList = new ArrayList<>();
//獲取IReceiptHandler接口的實(shí)現(xiàn)類
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;
}
}
至此,該方案完美符合了開(kāi)閉原則,如果新增一個(gè)回執(zhí)類型,只需要添加一個(gè)新的回執(zhí)處理器即可,無(wú)需做其它改動(dòng)。如新加了MT6666的回執(zhí),代碼如下
public class Mt6666ReceiptHandler implements IReceiptHandler {
@Override
public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) {
if (StringUtils.equals("MT6666",receipt.getType())) {
System.out.println("解析報(bào)文MT6666:" + receipt.getMessage());
}
//處理不了該回執(zhí)就往下傳遞
else {
handleChain.handleReceipt(receipt);
}
}
}
策略模式+注解
此方案其實(shí)和上述沒(méi)有太大異同,為了能符合開(kāi)閉原則,通過(guò)自定義注解的方式,標(biāo)記處理者類,然后反射獲取到該類集合,放到Map容器中,這里不再贅述
小結(jié)
if-else或switch case 這種分支判斷的方式對(duì)于分支邏輯不多的簡(jiǎn)單業(yè)務(wù),還是直觀高效的。對(duì)于業(yè)務(wù)復(fù)雜,分支邏輯多,采用適當(dāng)?shù)哪J郊记?,?huì)讓代碼更加清晰,容易維護(hù),但同時(shí)類或方法數(shù)量也是倍增的。我們需要對(duì)業(yè)務(wù)做好充分分析,避免一上來(lái)就設(shè)計(jì)模式,避免過(guò)度設(shè)計(jì)!
來(lái)源:cnblogs.com/DiDi516/p/11787257.html