Reconstruction of a variety of methods if statement

1 Introduction

Early in the project development phase if / else statements tend to be more simple, and with the increase in traffic and the passage of time, if / else points will be getting longer and longer. Next, how to reconstruct if / else to make a detailed analysis.

2. Case Studies

We often encounter a number of conditions involving business logic, and each requires a different treatment, we Calculator class as a demonstration sample. There is a method, which takes two numbers and an operator as input, and returns the corresponding results according to the operation:

public int calculate(int a, int b, String operator) {
    int result = Integer.MIN_VALUE;
 
    if ("add".equals(operator)) {
        result = a + b;
    } else if ("multiply".equals(operator)) {
        result = a * b;
    } else if ("divide".equals(operator)) {
        result = a / b;
    } else if ("subtract".equals(operator)) {
        result = a - b;
    }
    return result;
}
复制代码

We can also use a switch statement to achieve it:

public int calculateUsingSwitch(int a, int b, String operator) {
    switch (operator) {
    case "add":
        result = a + b;
        break;
    // other cases    
    }
    return result;
}
复制代码

In the real project development, if the statement could become bigger and more complex, this time the switch statement is not very suitable, we will introduce a better model to solve if / else problem.

3. reconstruction

3.1 factory class

if / else similar operation is performed in each branch, we return to the plant by the method of the type specified object and perform operations based on the specific object behavior.

Let's define Operation with a single interface to apply a method:

public interface Operation {
    int apply(int a, int b);
}
复制代码

The method of the two numbers as input and returns the result. Let's define a class for execution added:

public class Addition implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}
复制代码

We will now implement a factory class, which returns an instance of the given Operation operator:

public class OperatorFactory {
    static Map<String, Operation> operationMap = new HashMap<>();
    static {
        operationMap.put("add", new Addition());
        operationMap.put("divide", new Division());
        // more operators
    }
 
    public static Optional<Operation> getOperation(String operator) {
        return Optional.ofNullable(operationMap.get(operator));
    }
}
复制代码

Now, the Calculator class, we can query the factory for related operations:

public int calculateUsingFactory(int a, int b, String operator) {
    Operation targetOperation = OperatorFactory
      .getOperation(operator)
      .orElseThrow(() -> new IllegalArgumentException("Invalid Operator"));
    return targetOperation.apply(a, b);
}
复制代码

In this example, we have seen how loosely coupled objects will delegate the responsibility to provide the factory class, but there may be nested if statements just shifted to a factory class, we can maintain a repository objects in a Map, you can query the into the store to quickly find, so the design of the operationMap objects OperatorFactory.

3.2 Enumeration

In addition to using Map addition, we can also use Enum to mark specific business logic. After that, we can use them in nested if statements or switch case statement. Or, we can plant them as objects and to develop strategies to implement the relevant business logic. This reduces the number of nested if statements and Enum entrusted to a single value.

Let's see how we can achieve it. First of all, we need to define our enumeration:

public enum Operator {
    ADD, MULTIPLY, SUBTRACT, DIVIDE
}
复制代码

We can choose to use different conditions as enumerated values ​​in nested if statements or switch case, the following will introduce a logical alternative to delegate Enum itself.

First Enum value defined for each method and calculated. E.g:

ADD {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
},
// other operators
 
public abstract int apply(int a, int b);
复制代码

Then Calculator class, we can define a method of executable operations:

public int calculate(int a, int b, Operator operator) {
    return operator.apply(a, b);
}
复制代码

Now, we can use Operator.valueOf () method of the String value into an Operator to call the method:

@Test
public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(3, 4, Operator.valueOf("ADD"));
    assertEquals(7, result);
}
复制代码

3.3 Command Mode

Command Mode is another way to solve the nested if statements, we can design a Calculator # calculate method for receiving commands executable.

We first define our Command interface:

public interface Command {
    Integer execute();
}
复制代码

Next, let's implement a AddCommand:

public class AddCommand implements Command {
    // Instance variables
 
    public AddCommand(int a, int b) {
        this.a = a;
        this.b = b;
    }
 
    @Override
    public Integer execute() {
        return a + b;
    }
}
复制代码

Finally, let us accept the introduction of a new method in Calculator and Command execution:

public int calculate(Command command) {
    return command.execute();
}
复制代码

Next, we can calculate by instantiating AddCommand call and send it to the Calculator # calculate method:

@Test
public void whenCalculateUsingCommand_thenReturnCorrectResult() {
    Calculator calculator = new Calculator();
    int result = calculator.calculate(new AddCommand(3, 7));
    assertEquals(10, result);
}
复制代码

3.4 Rules Engine

When we finally write a lot of nested if statements, each condition describes a business rules must be evaluated in order to deal with the correct logic. Such rules engine acquired from the main code complexity.

Let's demonstrate by designing a simple RuleEngine, the RuleEngine Expression handled by a set of rules, and returns the result of the selected rule. First, we'll define a Rule interfaces:

public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
}
复制代码

Secondly, let us achieve a RuleEngine:

public class RuleEngine {
    private static List<Rule> rules = new ArrayList<>();
 
    static {
        rules.add(new AddRule());
    }
 
    public Result process(Expression expression) {
        Rule rule = rules
          .stream()
          .filter(r -> r.evaluate(expression))
          .findFirst()
          .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule"));
        return rule.getResult();
    }
}
复制代码

Expression RuleEngine the receiving object and returns a result. Now, let's designed as a set Expression class contains two Integer objects Operator:

public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;        
}
复制代码

Finally, let us define a custom AddRule class, the class is only evaluated when specifying ADD operation:

public class AddRule implements Rule {
    @Override
    public boolean evaluate(Expression expression) {
        boolean evalResult = false;
        if (expression.getOperator() == Operator.ADD) {
            this.result = expression.getX() + expression.getY();
            evalResult = true;
        }
        return evalResult;
    }    
}
复制代码

We will now use the Expression call RuleEngine:

@Test
public void whenNumbersGivenToRuleEngine_thenReturnCorrectResult() {
    Expression expression = new Expression(5, 5, Operator.ADD);
    RuleEngine engine = new RuleEngine();
    Result result = engine.process(expression);
 
    assertNotNull(result);
    assertEquals(10, result.getValue());
}
复制代码

4 Conclusion

We reconstructed by the above method if / else statement is not limited to the switch / casef embodiment, also explored different ways to simplify complex code and replaced by using nested if statements effective design patterns.

Guess you like

Origin juejin.im/post/5d3be67d5188251b55609058