Java中如何消除过多条件判断

Java中如何消除过多条件判断

条件判断结构是任何语言中的关键内容。但是如果编写了大量嵌套if语句,会使得代码更加复杂和难以维护。本文提供多种方式代替嵌套if语言,使代码更简洁。

案例说明

通常我们遇到涉及很多条件的业务逻辑,每个条件需要处理不同逻辑。为了演示方便,我们使用Calculator类,其中计算方法带两个操作数和一个操作符作为输入参数并返回操作结果:

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语言并实现其处理逻辑。

重构代码

让我们看看如何替代复杂的if语句,使其更简单且代码易维护。

工厂类

很多时候我们遇到的条件判断结构中每个分支需要处理类似的操作,这给我们一个机会————抽象工厂方法基于具体对象行为返回给定类型的对象并执行相应操作。下面示例中,我们定义包括单个apply方法的Operation接口:

public interface Operation {
    int apply(int a, int b);
}

该方法包括两个操作数作为输入参数并返回操作结果。然后我们定义累加操作的实现:

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

现在实现工厂类,基于给定的操作返回相应Operation的实例:

public class OperatorFactory {
    static Map operationMap = new HashMap<>();
    static {
        operationMap.put("add", new Addition());
        operationMap.put("divide", new Division());
        // more operators
    }
 
    public static Optional 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);
}

通过在map中维护多个对象实现快速查找。在运行时初始化Map并配置相应值用于查找,请看OperatorFactory.getOperation(operator)方法。
我们看到把责任委托给工厂类避免过度耦合。但有可能把if语句简单地转移至工厂类,这不是我们的目的。

使用枚举

除了使用Map,我们也可以使用枚举(Enum)标签化特定业务逻辑,然后可以在if或switch语句中使用。当然也可以在对象工厂中使用并执行相关业务逻辑。这样也可以减少if语句数量并委托责任给各个枚举值。下面请看示例如何实现,我们需要定义枚举:

public enum Operator {
    ADD, MULTIPLY, SUBTRACT, DIVIDE
}

我们看到每个值代表不同操作的标签用于后续计算。可以在if或switch语句中使用,但也可以直接把责任委托给枚举自己,下面我们给每个枚举值订一个方法实现相应计算:

public enum Operator {

    ADD {
        @Override
        public int apply(int a, int b) {
            return a + b;
        }
    },

    MULTIPLY {
        @Override
        public int apply(int a, int b) {
            return a * b;
        }
    },

    SUBTRACT {
        @Override
        public int apply(int a, int b) {
            return a - b;
        }
    };

    // ...

    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()方法把转换字符串值转成Operation枚举类型执行方法:

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

命令模式

在前面的示例中,我们看到使用工厂类并根据给定操作返回相应业务逻辑对象,然后在Calculator中业务逻辑对象则用于执行计算。

我们也可以设计Calculator#calculate 方法接收命令接口类型作为输入被执行,这是消除if语句的另一种方案,请看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);
}

规则引擎

当消除大量if语句时,每个条件描述一个业务规则,根据条件处理正确的逻辑。使用规则引擎可以消除主要复杂代码,规则引擎评估规则并基于输入返回结果。
下面通过示例进行说明,设计简单的RuleEngine类,其通过一组Rule处理Expression并返回每个规则的执行结果。首先定义Rule接口:

public interface Rule {
    boolean evaluate(Expression expression);
    Result getResult();
}

其次,实现RuleEngine:

public class RuleEngine {
    private static List 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对象并返回Result。Expression类包括两个Integer对象和Operator对象。

public class Expression {
    private Integer x;
    private Integer y;
    private Operator operator;        
}

然后再定义相关规则实现,自定义AddRule类,其仅仅响应指定的Add Operator:

public class AddRule implements Rule {

    private int result;

    @Override
    public boolean evaluate(Expression expression) {
        boolean evalResult = false;
        if (expression.getOperator() == Operator.ADD) {
            this.result = expression.getX() + expression.getY();
            evalResult = true;
        }
        return evalResult;
    }

    @Override
    public Result getResult() {
        return new Result(result);
    }
}

最后使用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());
}

总结

本文我们探讨多种方法消除复杂代码,学习有效使用设计模式代替复杂的if语句。

你可能感兴趣的:(设计模式)