多种方法重构if语句

1. 前言

项目开发初期阶段if/else语句通常比较简单,而后随着时间的推移和业务量的增长,if/else分之会愈来愈长。下面对如何重构if/else作出了详细分析。java

2. 案例研究

咱们常常遇到涉及不少条件的业务逻辑,而且每一个都须要不一样的处理,咱们以Calculator类做为演示样例。有一个方法,它接受两个数字和一个运算符做为输入项,并根据操做返回相应结果:express

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;
}
复制代码

咱们也能够使用switch语句来实现它:设计模式

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

在真正的项目开发中,if语句可能会变得更大,更复杂,这时候switch语句并非很适合,下面将介绍更好模式来解决if/else问题。app

3. 重构

3.1 工厂类

if/else每一个分支中执行相似的操做,咱们经过工厂方法返回给指定类型的对象并基于具体对象行为执行操做。ide

让咱们定义一个具备单个apply方法的Operation接口:this

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

该方法将两个数字做为输入并返回结果。让咱们定义一个用于执行添加的类:lua

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

咱们如今将实现一个工厂类,它根据给定的运算符返回Operation的实例:spa

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));
    }
}
复制代码

如今,在Calculator类中,咱们能够查询工厂以获取相关操做:设计

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

在这个例子中,咱们已经看到了如何将责任委托给工厂类提供的松散耦合对象,可是有可能嵌套的if语句只是转移到了工厂类,咱们能够在Map中维护一个对象存储库,能够查询该存储库以进快速查找,因此设计了OperatorFactory中的operationMap对象。code

3.2 使用枚举

除了使用Map以外,咱们还能够使用Enum来标记特定的业务逻辑。以后,咱们能够在嵌套的if语句或switch case语句中使用它们。或者,咱们也能够将它们用做对象的工厂并制定策略以执行相关的业务逻辑。这样能够减小嵌套if语句的数量并委托给单个Enum值。

让咱们看看咱们如何实现它。首先,咱们须要定义咱们的枚举:

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

咱们能够选择在嵌套的if语句或switch case中使用枚举值做为不一样的条件,下面将介绍一种将逻辑委托给Enum自己的替代方法。

首先为每一个Enum值定义方法并进行计算。例如:

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

而后在Calculator类中,咱们能够定义一个可执行操做的方法:

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

如今,咱们能够经过使用Operator.valueOf()方法将String值转换为Operator来调用该方法:

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

3.3 命令模式

命令模式是解决嵌套if语句的另外一种方法,咱们能够设计一个Calculator#calculate方法来接受可执行的命令。

咱们首先定义咱们的Command接口:

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

接下来,让咱们实现一个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;
    }
}
复制代码

最后,让咱们在Calculator中引入一个接受并执行Command的新方法:

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

接下来,咱们能够经过实例化AddCommand调用计算并将其发送到Calculator#calculate方法:

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

3.4 规则引擎

当咱们最终编写大量嵌套if语句时,每一个条件都描述了一个业务规则,必须对其进行评估才能处理正确的逻辑。规则引擎从主代码中获取了这种复杂性。

让咱们经过设计一个简单的RuleEngine来进行演示,该RuleEngine经过一组规则来处理Expression,并返回所选规则的结果。首先,咱们将定义一个Rule接口:

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

其次,让咱们实现一个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();
    }
}
复制代码

所述RuleEngine接受一个Expression对象并返回结果。如今,让咱们将Expression类设计为一组包含两个Integer对象的Operator:

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

最后让咱们定义一个自定义的AddRule类,该类仅在指定ADD操做时进行求值:

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;
    }    
}
复制代码

咱们如今将使用Expression调用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 结论

经过上述方法咱们重构if/else语句不会在仅仅局限于switch/casef方式,也探索了用不一样的方法来简化复杂的代码和经过使用有效的设计模式来替换嵌套的if语句。

相关文章
相关标签/搜索