关于java:来吧用设计模式来干掉-ifelse-啊

32次阅读

共计 11821 个字符,预计需要花费 30 分钟才能阅读完成。

前言

物流行业中,通常会波及到 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 这种分支判断的形式对于分支逻辑不多的简略业务,还是直观高效的。对于业务简单,分支逻辑多,采纳适当的模式技巧,会让代码更加清晰,容易保护,但同时类或办法数量也是倍增的。咱们须要对业务做好充沛剖析,防止一上来就设计模式,防止适度设计!

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

正文完
 0