preface

Although it is necessary, abusing if else can do a lot of damage to the readability and maintainability of code, and thus to the entire software system.

There are a lot of new technologies and concepts in software development, but if… Else the basic form of the program hasn’t changed much.

The use of if else is useful not only now, but also in the future. Today we’re going to take a look at how to clean up your code by killing if else.

In Web development, we often use fields in database tables as “tags” to represent multiple “states”, such as:

We take the online shopping process of some treasure as an example for analysis. In the order table, the ZT field is used to represent the status of the order. Common states are as follows:

When we want to query various types of orders according to the conditions, only need an interface, in the front of the corresponding status code can be passed. In the DAO layer, the following statements are used to query:

select * from orders where zt = #{zt}Copy the code

How to have high scalability?

Suppose there are several “requirements that are not requirements” :

  1. I want the order to be sorted by the time the order shipped or is expected to arrive, and the rest by the time the order was created
  2. You want to separate the status of “waiting for delivery” into two states: “user did not receive the delivery” and “user received the delivery but did not click the confirm receipt button”

What’s the conventional way?

  • Requirement 1 (different states are handled differently) : This is easy, just add a judgment in the Sevice layer, the rest of the code does not change, code as follows:
// 2 indicates goods to be receivedif(zt == 2){// By demand, by order shipping time or expected delivery time}else{// Orders in other states are sorted by order creation time}Copy the code

This code is small enough to change, but what if I wanted to sort all the different status orders differently? You might write the following code

if(zt==0){// Order processing code to be paid... }else if(zt==1){  }else if(zt==2){  }else if(zt==3){  }else if(zt==4){  }Copy the code

The above code is too low, some friends may use switch to optimize (I won’t write the code here, because there is no difference).

  • Requirement 2 (add a new state representation) : This is easy, just add a new state in the if-else or switch code above.

Think about how to kill if-else?

The above method can fulfill our requirements, but it has the following shortcomings:

  1. Faced with “all kinds of strange requirements”, we had to change the above code frequently, and over time, it would become a mess. We don’t even want to look at the code anymore;
  2. If we add a new state representation, that is, a new state meaning representation for the ZT field, we have to add if-else, which is too complicated.

Use policy patterns to solve if-else problems

Yes, using the policy pattern is a good way to solve the problem of doing too much state judging code. For example, each of the above if-else code is split into a class or method for processing.

I’m not going to write the main code, because I’m going to do the main thing, but the only way I’m going to do this is if else, decouple the code to a certain extent. But it doesn’t really solve the if-else problem, and it’s the most common solution on the web.

If you don’t know much about strategy patterns, check out this article, and you’ll see how it works below. The way to learn strategic patterns

Try using Spring to work with the policy pattern

One of the principles of programming, “open for extension, closed for modification,” defines an interface class for querying a list of orders in different states. As follows:

Public interface OrderService {/** * query the order list of the corresponding status * @param zt * @return*/ List<Order> getOrderList(String zt); }Copy the code

Then create different implementation classes based on different order states. For example, the order query class “to be paid” looks like this:

Although the following naming is an example of a mistake, it is well understood

@Service("orderServiceDfk") // This name is really unfriendly, But I believe you can understand the public class PendingParymentOrderSeviceImpl implements the OrderService {/ * * * check for payment order list * * @ param zt * @return*/ @override public List<Order> getOrderList(String zt)return null;    }Copy the code

The zT value is different from the zT value in the front end, and the zT value is different from the zT value in the back end. However, we can completely remove if-else through Spring.

Our Controller layer code is as follows:

@RestControllerpublic class OrderController {  private String orderServiceBeanNamePrefix = "orderService";  @RequestMapping("getOrderList/{zt}")  public List<Order> getOrderList(@PathVariable("zt"String zt) {// Get the corresponding processing state of the bean to process // with this line of code, completely solvedif- the else judgment logic OrderService OrderService = (OrderService) SpingContext. GetBean (orderServiceBeanNamePrefix + zt); List<Order> orderList = orderService.getOrderList();return orderList;  }}Copy the code

We used a utility class to get the bean from the Spring container. The code is as follows:

/** ** The principle is very simple, we write a class to implement this interface, see the Spring lifecycle details * Spring will automatically call itsetApplicationContext method, Pass in the Spring container context * we'll save the Spring context here * * @date 2020/5/18 * @auther Lyn4ever */ @ComponentPublic Class SpingContext implements ApplicationContextAware { private static ApplicationContext applicationContext; /** * Get bean * @param name * @ from Spring container according to namereturn     */    public static Object getBean(String name){        return applicationContext.getBean(name);    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        System.out.println("I saved the Spring context");        applicationContext = applicationContext;    }}Copy the code

Conclusion:

The solution to if-else is to use the policy pattern and use different classes to handle the logic for different “states” of orders, so that it can be “decoupled” nicely. However, if we add a “state representation”, we add if-else to the main logic to determine which class to use.

There are many ways to solve the if-else problem: Abstract factories are a good way to do this. The same problem can be solved by using Spring inversion of control. The benefits of this are as follows:

  1. “Real” addresses if-else irrelevant to our business;
  2. There is no need for the front and back end to state “agreed”, before 0 means “to be paid”, 1 means “to be shipped” such an operation, if you remember wrong, there will be a big problem. It is now represented by a specific string, meaning that the front end passes in the bean that it wants to solve the solution to, eliminating the “complex and error-prone convention” part.

As mentioned in the introduction, if else is an important part of the code, but excessive and unnecessary use of if else will negatively affect the readability and extensibility of the code, and then affect the entire software system.

The ability to “kill” if else reflects the programmer’s comprehensive application ability of software reconstruction, design pattern, object-oriented design, architectural pattern, data structure and other technologies, and reflects the programmer’s internal work.

Use if else wisely, not without design, not over design. These comprehensive and reasonable use of technology require programmers to constantly explore and summarize in the work.