Strategy + enumeration to eliminate if-else elegantly

Foreword:

I believe that when you first came into contact with JAVA object-oriented programming, if you encountered a large number of process judgment statements, almost the whole screen was filled with if-else statements. There were so many that you forgot where the head is and where the tail is. So, how to avoid using if-else extensively in object-oriented programming? It can replace a large number of if-else statements, has good readability and scalability, and can appear lightweight. I recommend using strategy + enumeration to eliminate if-else. How to use it, let’s give an example to illustrate.

Project scenario:

For example: Project scenario: If there is such a requirement, it is necessary to implement a memo function to know what to do within seven days of the week.

Problem description

 1 //if-else form judgment
 2 public String getToDo(String day){<!-- -->
 3 if("Monday".equals(day)){<!-- -->
          ...omit complex statements
 4 return "English class today";
 5 }else if("Tuesday".equals(day)){<!-- -->
          .....Omit complex statements
          return "I have Chinese class today";
 7 }else if("Wednesday".equals(day)){<!-- -->
         ...omit complex statements
 8 return "I have math class today";
 9 }else if("Thursday".equals(day)){<!-- -->
         ...omit complex statements
10 return "Music class today";
11 }else if("sunday".equals(day)){<!-- -->
         ...omit complex statements
12 return "Take a programming class today";
13 }else{<!-- -->
14 Line 10086 is omitted here...
15}
16}

We may suddenly think of using if-else to solve it, and of course there is no problem. But in business logic, a small amount of this kind of code is okay. If there are hundreds of judgments, the entire business logic may be full of if-else, which is neither elegant nor redundant.

Solution:

At this time, you can consider using the strategy enumeration form to replace this bunch of process-oriented if-else implementations.
First, define a getToDo() calling method. If “Monday” is passed in, that is the parameter “Monday”.

public String getToDo(String day){<!-- -->
    CheckDay checkDay=new CheckDay();
    return checkDay.day(DayEnum.valueOf(day));
}

In the getToDo() method, a DayEnum enumeration element can be obtained through DayEnum.valueOf(“Monday”), and what is obtained here is Monday.

Next, execute checkDay.day(DayEnum.valueOf(“Monday”)) and you will enter the day() method. Here, a policy matching is made through dayEnum.toDo(). Note that DayEnum.valueOf(“Monday”) gets Monday in the enumeration. In this way, Monday.toDo() is actually executed, that is, toDo() in Monday will be executed.

public class CheckDay {<!-- -->
    public String day( DayEnum dayEnum) {<!-- -->
        return dayEnum.toDo();
    }
}

Why is the above execution process like this? Only by entering the DayEnum enumeration can we know what is going on.

public enum DayEnum {<!-- -->
    Monday {<!-- -->
        @Override
        public String toDo() {<!-- -->
            ...omit complex statements
            return "English class today";
        }
    },
    Tuesday {<!-- -->
        @Override
        public String toDo() {<!-- -->
            ...omit complex statements
            return "I have Chinese class today";
        }
    },
    Wednesday {<!-- -->
        @Override
        public String toDo() {<!-- -->
            ...omit complex statements
            return "I have math class today";
        }
    },
    Thursday {<!-- -->
        @Override
        public String toDo() {<!-- -->
            ...omit complex statements
            return "Music class today";
        }
    };
    public abstract String toDo();

Among the DayEnum enumeration properties, an abstract method that implements toDo() is defined.

 public abstract String toDo();

In each enumeration element, the toDo() abstract method is overridden. In this way, when the passed parameter DayEnum.valueOf(“Monday”) flows to dayEnum.toDo(), it essentially finds the enumeration element corresponding to Monday’s definition in the DayEnum enumeration, and then executes its internally rewritten toDo() method. .

Expressed in the form of if-esle, it is similar to “Monday”.equals(day). When the match is true, its internal content can be obtained.

Summary

Strategy enumeration is the use of strategy mode in enumeration. The so-called strategy mode means that you are given a key and can be immediately guided to find the door that can be opened according to a certain agreed method.

For example, if the key I gave you is called “Monday”, then you can use the agreed method dayEnum.toDo() to immediately find the Monday door in the enumeration, then enter the door and do what you want to do toDo(), Among them, the rooms behind each door have different functions, but they all have the same abstract function-toDo(), that is, the common places in each room are functions that can be used to do something, but what specific things can be done, It’s different.

In the case of this article, toDo() in each door can return different strings according to different strategy modes, for example, “I have an English class today”, “I have a Chinese class today”, etc.

It can be seen that extracting process judgments into policy enumerations can also decouple a bunch of judgments to avoid a large number of dense and redundant if-else in the business code logic. Here, there will be a situation, if there are multiple judgments that repeat the same function, for example, in if-else, it is like this

public String getToDoByIfElse(String day){<!-- -->
    if("Monday".equals(day)||"Tuesday".equals(day)||"Wednesday".equals(day)){<!-- -->
        ...omit complex statements
        return "English class today";
    }else if("Thursday".equals(day)){<!-- -->
        ...
    }
}

So, how should it be used under strategy enumeration to avoid code redundancy?

You can refer to the following ideas, set up an internal policy enumeration, and point external references with the same function to the same internal enumeration element, so that repeated functions can be called.

public enum DayEnum {<!-- -->
    //Point to the same property of the internal enumeration to perform the same repeated function
    Monday("Monday", Type.ENGLISH),
    Tuesday("Tuesday", Type.ENGLISH),
    Wednesday("Wednesday", Type.ENGLISH),
    
    Thursday("Thursday", Type.CHINESE);
    private final Type type;
    private final String day;
    DayEnum(String day, Type type) {<!-- -->
        this.day = day;
        this.type = type;
    }
    String toDo() {<!-- -->
        return type.toDo();
    }
    /**
     * Internal policy enumeration
     */
    private enum Type {<!-- -->
        ENGLISH {<!-- -->
            @Override
            public String toDo() {<!-- -->
                ...omit complex statements
                return "English class today";
            }
        },
        CHINESE {<!-- -->
            @Override
            public String toDo() {<!-- -->
                ...omit complex statements
                return "I have Chinese class today";
            }
        };
        public abstract String toDo();
    }
}

If you want to expand its judgment process, you only need to directly add an attribute and internal toDo (implementation) to the enumeration to add a new judgment process. Externally, you can still use the same entry dayEnum.toDo().

Maybe, there will be such a question: Why is an abstract method defined in an enumeration implemented in each enumeration element?

This function is similar to how a subclass inherits from a parent class. DayEnum is similar to a parent class, and the elements in the DayEnum enumeration are equivalent to its subclasses. When the abstract method toDo() is defined in the parent class, the inherited subclass will implement the toDo() method by default. In this way, the enumeration can be written like this:

 private enum Type {<!-- -->
        ENGLISH {<!-- -->
            @Override
            public String toDo() {<!-- -->
                return "English class today";
            }
        };
        public abstract String toDo();
    }

All in all, using policy enumeration can handle various complex judgments very flexibly, and has good readability and scalability. It is more like functional programming, that is, by passing in a parameter, you can get the value returned in the corresponding mode.

If if-else is used in large quantities in business logic in Java, it is process-oriented, because if-else in business logic is judged one if after another from top to bottom, and a breakpoint is set on each if. After debugging, you will understand that it is actually process-oriented.

It can be seen that if there are a lot of if-else in the project, it will really affect the performance. Although this performance is negligible, wouldn’t it be better to have a better alternative?