I’m sure you’ve read many articles on how to get rid of if…else, and I know you all know a lot about how to get rid of if…else. .else articles, and I also know that we all know many ways to get rid of if…. .else, such as Option, Strategy Patterns, etc., but I believe that this article is the most comprehensive and complete article on how to remove if…else. . else article, some of the methods I believe that some partners certainly do not know, I do not sell the secret, directly into the subject, how to kill if…. . else.

 Method 1: Early return

 Suppose there is the following code:

if (condition){
  doSomething;
} else {
  return;
}


This is the kind of code we usually use to return early, removing the unnecessary else.

if (!condition){
  return
}

doSomething;


This approach is generally only suitable for if…else with simple branching structure. . else, and we can return early to get rid of some of the unnecessary if…else. . else.

 Method 2: Enumeration


Enumerations can actually be de-enumerated by removing the if…. .else as follows:

String orderStatusDes;
if ("1".equals(orderStatus)) {
    orderStatusDes = "1";
} else if ("2".equals(orderStatus)) {
    orderStatusDes = "2";
} else if ("3".equals(orderStatus)) {
    orderStatusDes = "3";
} else if ("4".equals(orderStatus)) {
    orderStatusDes = "4";
} else if ("5".equals(orderStatus)) {
    orderStatusDes = "5";
}


Some of my friends may say, “Shit, who would write this kind of code? Don’t be so absolute, Damien has been working for so long and still sees people who have been working for 5 or 6 years writing this kind of code. This type of code is very suitable for enumeration to solve.

 Start by defining an enum class:

@Getter
@AllArgsConstructor
public enum OrderStatusEnum {
    UN_PAID("1",),
    PAIDED("2",),
    SENDED("3",),
    SINGED("4",),
    EVALUATED("5",);

    private String status;

    private String statusDes;

    static OrderStatusEnum of(String status) {
        for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) {
            if (statusEnum.getStatus().equals(status)) {
                return statusEnum;
            }
        }
        return null;
    }
}

 With this enumeration, the above code can be directly optimized to one line of code:

String orderStatusDes = OrderStatusEnum.of(orderStatus).getStatusDes();


Of course, in the actual project, this is not the best way to deal with the best way should be in the database there is a code value configuration table, and then loaded into the system cache to come, in the code to fetch the value. Of course, enumeration is also a good solution.

 Option 3: Optional Sentencing


I’m sure you guys have projects where there is a non-null judgment, and if it’s null, an exception or return is thrown.

Order order = getOrderById(id);
if (order == null) {
    return "-1";
} else {
    return order.getOrderStatus();
}


For this kind of code we can use Optional to solve it very elegantly.

return Optional.ofNullable(order).map(o -> o.getOrderStatus()).orElse("-1");

 Isn’t this way very elegant and classy. One last addition:

 Preventing NPEs is a basic programmer’s skill.

 Option 4: Table-driven approach


The table-driven method, is a method that allows you to look up information in a table without having to use excessive if…. . else to find them out. Here’s how:

if ("code1".equals(action)) {
    doAction1();
} else if ("code2".equals(action)) {
    doAction2();
} else if ("code3".equals(action)) {
    doAction3();
} else if ("code4".equals(action)) {
    doAction4();
} else if ("code5".equals(action)) {
    doAction5();
}

 The optimization method is as follows:

Map<String, Function<?> action> actionMap = new HashMap<>();
action.put("code1",() -> {doAction1()});
action.put("code2",() -> {doAction2()});
action.put("code3",() -> {doAction3()});
action.put("code4",() -> {doAction4()});
action.put("code5",() -> {doAction5()});

actionMap.get(action).apply();


This is actually not a very good way to do it, as it can appear to be very bloated code. One morphing solution is to abstract doAction() into classes. It is as follows:


public interface ActionService {
    void doAction();
}

public class ActionService1 implements ActionService{
    public void doAction() {
        //do something
    }
}

Map<String, ActionService> actionMap = new HashMap<>();
action.put("code1",new ActionService1());
action.put("code2",new ActionService2());
action.put("code3",new ActionService3());
action.put("code4",new ActionService4());
action.put("code5",new ActionService5());

actionMap.get(action).doAction();

 Isn’t this way more elegant!

 Option 5: Strategy Pattern + Factory Methods


The strategy pattern + factory method is a very common solution to the problem of if…else. . else is a very common solution to the problem of if…else, and is somewhat similar to the table-driven approach above. There are several steps to using the Strategy Pattern + Factory Methods, as shown in the example above:

  •  Abstracting the condition module to a public interface, the policy interface
public interface ActionService {
    void doAction();
}
  •  Based on each logic, define your own specific strategy implementation class as follows:
public class ActionService1 implements ActionService{
    public void doAction() {
        //do something
    }
}

public class ActionService2 implements ActionService{
    public void doAction() {
        //do something
    }
}

  •  The factory class, Unified Scheduler, is used to manage these policies as follows:
public class ActionServiceFactory {
    private ActionServiceFactory(){

    }

    private static class SingletonHolder{
        private static ActionServiceFactory instance=new ActionServiceFactory();
    }

    public static ActionServiceFactory getInstance(){
        return SingletonHolder.instance;
    }

    private static final Map<String,ActionService> ACTION_SERVICE_MAP = new HashMap<String, ActionService>();

    static {
        ACTION_SERVICE_MAP.put("action1",new ActionService1());
        ACTION_SERVICE_MAP.put("action2",new ActionService2());
        ACTION_SERVICE_MAP.put("action3",new ActionService3());
        ACTION_SERVICE_MAP.put("action4",new ActionService4());
        ACTION_SERVICE_MAP.put("action5",new ActionService5());
    }

    public static ActionService getActionService(String actionCode) {
        ActionService actionService = ACTION_SERVICE_MAP.get(actionCode);
        if (actionService == null) {
            throw new RuntimeException("非法 actionCode");
        }
        return actionService;
    }

    public void doAction(String actionCode) {
        getActionService(actionCode).doAction();
    }
}

 The singleton pattern implements the factory class.

 

ActionServiceFactory.getInstance().doAction("action1");


This kind of optimization is also very elegant, especially suitable for more branches and more complex logic code blocks, this way to decouple the branching logic from the business code is a very good solution.

 Option 6: Chain-of-responsibility model


You wouldn’t think that chain-of-responsibility patterns could be optimized for if…else, would you? . else. Chain of responsibility can be seen as a single linked table data structure, one object by one object to filter the conditions, if it meets the execution, and then end, does not meet the next node will be passed to the next node, if each object can not be processed, there is generally a final node to deal with the unity of the node.

 Let’s still use that example above as an example.

  •  Define the chain of responsibility to process the request node
public abstract class ActionHandler {


    protected ActionHandler successor;

    public void handler(String actionCode) {
        doHandler(actionCode);
    }


    protected ActionHandler setSuccessor(ActionHandler successor) {
        this.successor = successor;
        return this;
    }


    public abstract void doHandler(String actionCode);
}
  •  Define first and last nodes for some exceptions

public class HeadHandler extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if (StringUtils.isBlank(actionCode)) {
            throw new RuntimeException("actionCode");
        }

        successor.doHandler(actionCode);
    }
}

  •  Define node-specific implementation nodes for each node
public class ActionHandler1 extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action1".equals(actionCode)) {
            doAction1();
        } else {
           
            successor.doHandler(actionCode);
        }
    }
}

public class ActionHandler2 extends ActionHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action2".equals(actionCode)) {
            doAction2();
        } else {
          
            successor.doHandler(actionCode);
        }
    }
}


  •  Define the factory to build a complete chain of responsibility and be responsible for scheduling the
public class ActionHandlerFactory {
    
    private ActionHandler headHandler;
    
    private ActionHandlerFactory(){
        headHandler = new HeadHandler();
        ActionHandler actionHandler1 = new ActionHandler1();
        ActionHandler actionHandler2 = new ActionHandler2();
        ActionHandler actionHandler3 = new ActionHandler3();
        ActionHandler actionHandler4 = new ActionHandler4();
        ActionHandler actionHandler5 = new ActionHandler5();

        ActionHandler tailHandler = new TailHandler();         
       headHandler.setSuccessor(actionHandler1).setSuccessor(actionHandler2).setSuccessor(actionHandler3).
                setSuccessor(actionHandler4).setSuccessor(actionHandler5).setSuccessor(tailHandler);
    }

    private static class SingletonHolder{
        private static ActionHandlerFactory instance=new ActionHandlerFactory();
    }

    public static ActionHandlerFactory getInstance(){
        return SingletonHolder.instance;
    }
        
    public void doAction(String actionCode) {
        headHandler.doHandler(actionCode);
    }
}

 

ActionHandlerFactory.getInstance().doAction("action1");

 Program VII: Function


Function is a functional interface in Java 8, and by utilizing it we can greatly simplify our code, for example by using it we can easily remove our if…else. For example, we can easily remove our if…else’s from our code:


if (...) {
  throw new RuntimeException("...")
}

// if...else 
if(...) {
  doSomething1();
} else {
  doSomething2();
}


Now we’ll use Function to handle the two pieces of code above.

 Handling thrown exceptions

  •  Functional interface that defines the form in which exceptions are thrown
@FunctionalInterface
public interface ThrowExceptionFunction {

    void throwMessage(String message);
}


All that’s needed here is a functional interface like this, and the method doesn’t have a return value, it’s a consumer interface.

  •  Add judgment tool class
public class ValidateUtils {

    public static ThrowExceptionFunction isTrue(Boolean flag) {
        return (errorMessage) -> {
            if (flag) {
                throw new RuntimeException(errorMessage);
            }
        };
    }
}


The ValidateUtils class is also very simple, throwing an exception if the flag passed in is true. isTrue() The return value is also the ThrowExceptionFunction we just defined.

 

ValidateUtils.isTrue(flag).throwMessage("...");

 Isn’t it very simple to use?

 Handling if… .else Branching


Actually, I think it’s a bit of an oddity to use a Function to get rid of the if…else branching. .else branching I think is a bit of an oddity, because it’s very dependent on the Function function that we’re defining. For example, if we’re defining a method with only two arguments, then it can only handle two branches, and if…else is three branches, then you need to redefine the method. . else would require redefining the method. Here’s an example of two branches.

  •  Defining Functional Interfaces
@FunctionalInterface
public interface ActionHandler {
    void doActionHandler(ActionService trueActionService,ActionService falseActionService);
}


The functional interface defines a method, doActionHandler() , which takes two arguments, respectively:


  1. trueActionService: the action to be performed if true

  2. falseActionService: the action to be performed if it is false
  •  Defining judgment methods


Add a tool class to determine which method to execute when true and which method to execute when false.

public class ActionHandlerUtils {

    public static ActionHandler isTrue(Boolean flag) {
        return (trueActionService,falseActionService) -> {
            if (flag) {
                trueActionService.doAction();
            } else {
                falseActionService.doAction();
            }
        };
    }
}

 

ActionHandlerUtils.isTrue(true)
        .doActionHandler(() -> {
            //do true Something
        },() ->{
            //do false Something
    });

By lzz

Leave a Reply

Your email address will not be published. Required fields are marked *