java kill if-else

Preface Traditional approach - if-else branch strategy mode + Map dictionary responsibility chain mode strategy mode + annotation In the logistics industry, it usually involves the transmission of EDI messages (XML format files) and receipt of receipts. Every time an EDI message is sent, subsequent You will receive a receipt associated with it (identifying the flow status of the data in the third-party system).

Here are several types of receipts: MT1101, MT2101, MT4101, MT8104, MT8105, and MT9999. After receiving different receipt messages, the system will perform corresponding business logic processing. Of course, the actual business scenario is not that general. Here we use return receipt processing as a demonstration case.

Simulate a receipt class

@Data
public class Receipt {
    
    

    /**
     * 回执信息
     */
    String message;

    /**
     * 回执类型(`MT1101、MT2101、MT4101、MT8104、MT8105、MT9999`)
     */
    String type;

}

Simulate a receipt generator

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;
    }
}

Traditional approach-if-else branch

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
}

When encountering complex if-else branch business logic, we are accustomed to extracting a method or encapsulating it into an object to call, so that the entire if-else structure will not appear too bloated.

In the above example, when there are more and more types of receipts, there will be more and more branches else if. Every time a receipt type is added, the if-else branch needs to be modified or added, which violates the opening and closing principle (open to expansion, Modification is closed)

We know that the purpose of the strategy pattern is to encapsulate a series of algorithms that have common characteristics and can be replaced with each other. That is to say, the algorithm changes independently of the client using it. The client only relies on the strategy interface.

In the above scenario, we can extract the business logic of the if-else branch into various strategies, but it is inevitable that the client still needs to write some if-else logic for strategy selection. We can extract this logic to the factory Go to the class, this is the strategy pattern + simple factory, the code is as follows

Policy interface

/**
 * @Description: 回执处理策略接口
 * @Auther: wuzhazha
 */
public interface IReceiptHandleStrategy {
    
    

    void handleReceipt(Receipt receipt);

}

Strategy interface implementation class, which is the specific processor

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

}

Strategy context class (holder of the strategy interface)

/**
 * @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);
        }
    }
}

strategy factory

/**
 * @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;
    }
}

client

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);
        }
    }
}

Parsing the message MT2101: This is the MT2101 receipt message. Parsing the message MT8104: This is the MT8104 receipt message.

Since our purpose is to eliminate if-else, we need to transform the ReceiptHandleStrategyFactory strategy factory and use a dictionary to store my strategy. Map has a key-value structure, so using Map is a good choice.

With a slight modification, the code is as follows

/**
 * @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);
    }
}

After transforming the strategy pattern + simple factory solution, we have eliminated the if-else structure. Whenever a new receipt comes, we only need to add a new receipt processing strategy and modify the Map collection in ReceiptHandleStrategyFactory.

If you want to make the program comply with the opening and closing principle, you need to adjust the acquisition method of the processing strategy in ReceiptHandleStrategyFactory, obtain all IReceiptHandleStrategy implementation classes under the specified package through reflection, and then put them in the dictionary Map.

Chain of responsibility pattern
The chain of responsibility pattern is an object's behavior pattern. In the chain of responsibility pattern, many objects are connected to form a chain by each object's reference to its descendant. The request is passed up the chain until an object in the chain decides to handle the request.

The client making the request does not know which object in the chain ultimately handles the request, which allows the system to dynamically reorganize and assign responsibilities without affecting the client.

Return receipt handler interface

/**
 * @Description: 抽象回执处理者接口
 * @Auther: wuzhazha
 */
public interface IReceiptHandler {
    
    

    void handleReceipt(Receipt receipt,IReceiptHandleChain handleChain);

}
责任链接口

/**
 * @Description: 责任链接口
 * @Auther: wuzhazha
 */
public interface IReceiptHandleChain {
    
    

    void handleReceipt(Receipt receipt);
}

Responsibility link interface implementation class

/**
 * @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);
        }
    }
}

Responsibility chain handler container (if you use spring, you can obtain the subclass object of IReceiptHandler through dependency injection)

/**
 * @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;
    }

}

client

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);
        }
    }
}

Parsing the message MT2101: This is the MT2101 receipt message. Parsing the message MT8104: This is the MT8104 receipt message.

Through the chain of responsibility processing method, the if-else structure has also been eliminated by us. Whenever a new receipt comes, we only need to add the IReceiptHandler implementation class and modify the ReceiptHandlerContainer handler container. If we want to make the program comply with the opening and closing principle, You need to adjust the acquisition method of the handler in ReceiptHandlerContainer and obtain all IReceiptHandler implementation classes under the specified package through reflection.

A reflection tool class is used here to obtain all implementation classes of the specified interface.

/**
 * @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;
    }

}

Next, transform 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;
    }

}

At this point, the solution perfectly complies with the opening and closing principle. If you add a new receipt type, you only need to add a new receipt processor without making other changes. If a new receipt is added for MT6666, the code is as follows

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);
        }
    }
}

Strategy Mode + Annotations
This solution is actually not much different from the above. In order to comply with the opening and closing principle, the processor class is marked by custom annotations, and then obtained by reflection. Go to this type of collection and put it in the Map container. I won’t go into details here

Summary
The branch judgment method of if-else or switch case is intuitive and efficient for simple businesses with little branch logic. For complex businesses with a lot of branch logic, using appropriate pattern techniques will make the code clearer and easier to maintain, but at the same time, the number of classes or methods will also double. We need to fully analyze the business, avoid designing patterns at the beginning, and avoid over-designing!

Guess you like

Origin blog.csdn.net/weixin_44749255/article/details/133865710