多種方法重構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語句。

相關文章
相關標籤/搜索