In the logistics industry, EDI message (XML file) transmission and receipt are usually involved. Each SENT EDI message will be followed by an associated receipt (identifying the flow status of the data in the third-party system).

There are several types of acknowledgement packets: MT1101, MT2101, MT4101, MT8104, MT8105, and MT9999. After receiving different acknowledgement packets, the system performs corresponding service logic processing. Of course, the actual business scenario is not that general, and here is a demonstration case of receipt processing.

Simulate a return receipt class

@data public class Receipt {/** * return message */ String message; /** * Return receipt type (' MT1101, MT2101, MT4101, MT8104, MT8105, MT9999 ') */ Stringtype;

}
Copy the code

Simulate a return receipt generator

public class ReceiptBuilder {

    public static List<Receipt> generateReceiptListList<Receipt> receiptList = new ArrayList<>(); receiptList.add(new Receipt("This is MT2101 return receipt."."MT2101"));
        receiptList.add(new Receipt("This is MT1101 receipt."."MT1101"));
        receiptList.add(new Receipt("This is MT8104 return receipt."."MT8104"));
        receiptList.add(new Receipt("This is MT9999 receipt."."MT9999")); / /...returnreceiptList; }}Copy the code

Traditional if-else branch

List<Receipt> receiptList = ReceiptBuilder.generateReceiptList(); // loop processingfor (Receipt receipt : receiptList) {
    if (StringUtils.equals("MT2101",receipt.getType())) {
        System.out.println("Receipt of MT2101");
        System.out.println("Parse the contents of the receipt");
        System.out.println("Execute business logic");
    } else if (StringUtils.equals("MT1101",receipt.getType())) {
        System.out.println("Receipt of MT1101");
        System.out.println("Parse the contents of the receipt");
        System.out.println("Execute business logic");
    } else if (StringUtils.equals("MT8104",receipt.getType())) {
        System.out.println("Receipt of MT8104");
        System.out.println("Parse the contents of the receipt");
        System.out.println("Execute business logic");
    } else if (StringUtils.equals("MT9999",receipt.getType())) {
        System.out.println("Receipt of MT9999");
        System.out.println("Parse the contents of the receipt");
        System.out.println("Execute business logic");
        System.out.println(Push mail); } / /... There may be more to comeelse if
}
Copy the code

When the branch business logic of if-else is complicated, we are used to pulling it out of a method or encapsulating it into an object to call, so that the whole if-else structure doesn’t seem too bloated.

As in the example above, when there are more and more receipt types, there will be more and more branch else if. Every time you add a receipt type, you need to modify or add if-else branch, which violates the open closed principle (open for extension, closed for modification).

Policy mode +Map dictionary

As we know, the purpose of the policy pattern is to encapsulate a set of algorithms that are common and interchangeable, that is, to let the algorithm change independently of the clients that use it, relying solely on the policy interface.

In the above scenario, we can extract the business logic of the if-else branch into various policies, but inevitably we still need the client to write some if-else logic for policy selection. We can extract this logic into the factory class, which is the policy pattern + simple factory. The code is as follows

Policy interface

/** * @description: @auther: wuzhazha */ public interface IReceiptHandleStrategy { void handleReceipt(Receipt receipt); }Copy the code

The policy interface implementation class, that is, the concrete handler

public class Mt2101ReceiptHandleStrategy implements IReceiptHandleStrategy { @Override public void handleReceipt(Receipt  receipt) { System.out.println("Parse message MT2101:" + receipt.getMessage());
    }

}

public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy {

    @Override
    public void handleReceipt(Receipt receipt) {
        System.out.println("Parse message MT1101:" + receipt.getMessage());
    }

}

public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy {

    @Override
    public void handleReceipt(Receipt receipt) {
        System.out.println("Parse message MT8104:" + receipt.getMessage());
    }

}

public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy {

    @Override
    public void handleReceipt(Receipt receipt) {
        System.out.println("Parse message MT9999:"+ receipt.getMessage()); }}Copy the code

Policy context class (holder of policy interface)

/** * @description: context class, holding policy interface * @auther: wuzhazha */ public class ReceiptStrategyContext { private IReceiptHandleStrategy receiptHandleStrategy; @param receiptHandleStrategy */ public voidsetReceiptHandleStrategy(IReceiptHandleStrategy receiptHandleStrategy) {
        this.receiptHandleStrategy = receiptHandleStrategy;
    }

    public void handleReceipt(Receipt receipt){
        if(receiptHandleStrategy ! = null) { receiptHandleStrategy.handleReceipt(receipt); }}}Copy the code

Strategy factory

/ * * * @ Description: strategy factory * @ Auther: wuzhazha * / public class ReceiptHandleStrategyFactory {privateReceiptHandleStrategyFactory(){}

    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();
        }
        returnreceiptHandleStrategy; }}Copy the code

The client

Public class Client {public static void main(String[] args) {List<Receipt> receiptList = ReceiptBuilder.generateReceiptList(); ReceiptStrategyContext ReceiptStrategyContext = new ReceiptStrategyContext();for(Receipt receipt : ReceiptList) {// Obtain and set policy IReceiptHandleStrategy receiptHandleStrategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType()); receiptStrategyContext.setReceiptHandleStrategy(receiptHandleStrategy); . / / execution strategy receiptStrategyContext handleReceipt (receipt); }}}Copy the code

Parsing message MT2101: THIS is MT2101 acknowledgement message Wow Parsing message MT8104: This is MT8104 acknowledgement message Wow.

Because our goal is to eliminate the if – else, so here need to ReceiptHandleStrategyFactory strategy reforming, the factory was to use a dictionary to keep my strategy, and the Map with the key – value structure, using the Map is a good choice.

With a slight modification, the code is as follows

/** * @description: policy factory * @auther: wuzhazha */ public class ReceiptHandleStrategyFactory { private static Map<String,IReceiptHandleStrategy> receiptHandleStrategyMap; privateReceiptHandleStrategyFactory(){
        this.receiptHandleStrategyMap = new HashMap<>();
        this.receiptHandleStrategyMap.put("MT2101",new Mt2101ReceiptHandleStrategy());
        this.receiptHandleStrategyMap.put("MT8104",new Mt8104ReceiptHandleStrategy());
    }

    public static IReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){
        returnreceiptHandleStrategyMap.get(receiptType); }}Copy the code

After the strategy pattern + simple factory plan, we have to eliminate the structure of the if – else, every time a new receipt, only need to add a new receipt processing strategy, and modify the ReceiptHandleStrategyFactory Map collections.

If you want to make the program conforms to the open closed principle, you need to adjust the ReceiptHandleStrategyFactory processing policy access, through the reflection of the way, under the specified package for all IReceiptHandleStrategy implementation class, and then into a dictionary in the Map.

Systematic learning design patterns: Design pattern content aggregation

Chain of Responsibility model

Chain of responsibility is an object behavior pattern. In the chain of responsibility pattern, many objects are connected in a chain by each object’s reference to its next parent. Requests pass along the chain until one of the objects on the chain decides to process the request.

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

Receipt handler interface

/** * @description: abstract receipt handler * @auther: wuzhazha */ public interface IReceiptHandler { void handleReceipt(Receipt receipt,IReceiptHandleChain handleChain); }Copy the code

Liability link port

/** * @description: * @auther: wuzhazha */ public interface IReceiptHandleChain { void handleReceipt(Receipt receipt); }Copy the code

Responsible link interface implementation class

/** * @description: responsibility chain implementation class * @auther: Wuzhazha */ public class ReceiptHandleChain implements IReceiptHandleChain {private int index = 0; Private static List<IReceiptHandler> receiptHandlerList; The static {/ / object from the container to get the processor 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); }}}}Copy the code

Specific receipt handler

public class Mt2101ReceiptHandler implements IReceiptHandler {

    @Override
    public void handleReceipt(Receipt receipt, IReceiptHandleChain handleChain) {
        if (StringUtils.equals("MT2101",receipt.getType())) {
            System.out.println("Parse message MT2101:"+ receipt.getMessage()); } // If the receipt cannot be processed, pass it downelse {          
            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("Parse message MT8104:"+ receipt.getMessage()); } // If the receipt cannot be processed, pass it downelse{ handleChain.handleReceipt(receipt); }}}Copy the code

Responsibility chain handler container (if you’re using Spring, you can get the IReceiptHandler subclass object via dependency injection)

/** * @description: handler container * @auther: wuzhazha */ public class ReceiptHandlerContainer {privateReceiptHandlerContainer(){}

    public static List<IReceiptHandler> getReceiptHandlerList(){
        List<IReceiptHandler> receiptHandlerList = new ArrayList<>();
        receiptHandlerList.add(new Mt2101ReceiptHandler());
        receiptHandlerList.add(new Mt8104ReceiptHandler());
        returnreceiptHandlerList; }}Copy the code

The client

Public class Client {public static void main(String[] args) {List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();for(Receipt List) {// Receipt processing chain object ReceiptHandleChain ReceiptHandleChain = new ReceiptHandleChain(); receiptHandleChain.handleReceipt(receipt); }}}Copy the code

Parsing message MT2101: THIS is MT2101 acknowledgement message Wow Parsing message MT8104: This is MT8104 acknowledgement message Wow.

The IReceiptHandler implementation class is added and the ReceiptHandlerContainer handler is modified. In order to make the program open and close, the iReceipThandlerContainer handler is added. To obtain all IReceiptHandler implementation classes in the specified package, adjust the method of obtaining the handler from ReceiptHandlerContainer.

A reflection utility class is used to get all the implementation classes for the specified interface

/** * @description: reflection tool class * @auther: Wuzhazha */ public class ReflectionUtil {/** * private static final Set< class <? >> CLASS_SET; Static {// specify package loading path CLASS_SET = getClassSet("com.yaolong"); } /** * get the class loader * @return
     */
    public static ClassLoader getClassLoader() {returnThread.currentThread().getContextClassLoader(); } /** * load class * @param className Fully qualified name of the class * @param isInitialized Whether a static code block is executed after loading is complete * @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); }returncls; } public static Class<? > loadClass(String className) {return loadClass(className,true); } /** * get all classes in the specified package * @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(newFileFilter() {
            @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() {
        returnCLASS_SET; } /** * get all subclasses (or implementation classes) of a superClass (or interface) under the application package name * @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);
            }
        }
        returnclassSet; } /** * Get the class with an annotation in the application package name * @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); }}returnclassSet; }}Copy the code

Next, transform the ReceiptHandlerContainer

public class ReceiptHandlerContainer {

    private ReceiptHandlerContainer(){}

    public static List<IReceiptHandler> getReceiptHandlerList(){ List<IReceiptHandler> receiptHandlerList = new ArrayList<>(); // Obtain IReceiptHandler interface implementation Class 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(); }}}returnreceiptHandlerList; }}Copy the code

So far, the scheme perfectly conforms to the open and close principle, if a new receipt type, just need to add a new receipt processor, no other changes. If the receipt of MT6666 is added, 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("Parse message MT6666:"+ receipt.getMessage()); } // If the receipt cannot be processed, pass it downelse{ handleChain.handleReceipt(receipt); }}}Copy the code

Policy pattern + annotation

In order to comply with the open and closed principle, the handler class is marked by custom annotations, and then the class collection is reflected and put into the Map container, which will not be described here.

The if-else or switch case method of branch judgment is intuitive and efficient for simple business with little branch logic. For complex business, branching logic, the adoption of appropriate schema techniques will make the code clearer and easier to maintain, but at the same time the number of classes or methods is doubled. We need to do a good job of analyzing the business, avoid designing patterns in the first place, avoid over-designing!

Author: DiDi516

cnblogs.com/DiDi516/p/11787257.html

This article is only for learning, copyright belongs to the original author, if there is infringement, please contact delete.

I’ve compiled the interview questions and answers in PDF files, as well as a set of learning materials covering, but not limited to, the Java Virtual Machine, the Spring framework, Java threads, data structures, design patterns and more.

Follow the public account “Java Circle” for information, as well as quality articles delivered daily.