简体   繁体   English

Java:重构多个if语句

[英]Java: refactoring of multiple if statements

"Multiple if statements" is a standard code smell. “多个if语句”是标准代码的味道。 There's a bunch of approaches for how to refactor it. 有很多方法可以重构它。 In simple cases I try to use strategy pattern. 在简单的情况下,我尝试使用策略模式。 But recently I ran at the code where multiple if statements with int ranges were used. 但是最近我在代码中使用了多个带int范围的if语句。 And I have no ideas how to make this code clean. 而且我不知道如何使这段代码干净。

Here is an example of such a code: 这是这样的代码的示例:

public void calculate(int i) {
    if(i > 0 && i < 5) {
        // do smth
    } else if(i > 4 && i < 10) {
        //do smth
    } else if (i >= 10 && i <20 ) {
        //do smth
    }
    //...
    else if (i > 90 && i < 100) {
        //do smth
    }
}

I tried to extract every range to some logical unit in order to use strategy, but all these if statements were just moved out of this method and wouldn't disappear at all. 我尝试将每个范围提取到某个逻辑单元以使用策略,但是所有这些if语句只是移出了该方法,根本不会消失。

Is there any way to refactor such if statements (ie where int ranges are checked)? 有什么方法可以重构此类if语句(即检查int范围)?

This isn't the best example to make that point. 这不是说明这一点的最佳示例。

It's arguable that this code is relatively clean. 可以认为这段代码是相对干净的。 It certainly is easy to read, perhaps more so than a complex strategy pattern. 它当然很容易阅读,也许比复杂的策略模式更容易阅读。 I think of strategy when polymorphism comes into play. 我想到多态性发挥作用时的策略。

This example can easily be cleaned up using a Map , where the key is the max value in the range and the value is an interface type reference from the java.util.function package in JDK 8. Perhaps IntToDoubleFunction is what you need. 使用Map可以轻松清理该示例,其中的键是范围内的最大值,而值是JDK 8 java.util.function包的接口类型引用。也许IntToDoubleFunction是您所需要的。

Why does your calculate method appear to do nothing? 为什么您的计算方法似乎什么也不做? Shouldn't it return a calculation result? 它不应该返回计算结果吗?

private Map<Integer, IntToDoubleFunction> strategy = new TreeMap<Integer, IntToDoubleFunction>() {{
    put(5, new IntToDoubleFunction() { // add function here });
    put(90, new IntToDoubleFunction() { // add function here });
}};

void calculate(int input) {
    double result = 0.0;
    for (Integer maxValueInRange: this.strategy.keySet()) {
        if (input <= maxValueInRange) {
            result = this.strategy.get(maxValueInRange).applyAsDouble(input);
            break;
            // what happens to result?
        }
    }
}

I dont think there is a good way to do it without the if branching but you may skip the lower bound since you are using else if eg 我认为没有if分支是没有一个好的方法,但是您可能会跳过下限,因为您正在使用else if

public void calculate(int i) {
    if( i <= 0) {
        return;
    } else if(i < 5) {
        // do smth
    } else if(i < 10) {
        //do smth
    } else if (i <20 ) {
        //do smth
    }
    //...
    else if (i < 100) {
        //do smth
    }
}

Edit: Updated it to include the 0 case. 编辑:更新为包括0的情况。 Thanks Stultuske 感谢Stultuske

try to change 尝试改变

if(i > 0 && i < 5) {
    // do smth
} else if(i > 4 && i < 10) {
    //do smth
} else if (i >= 10 && i <20 ) {
    //do smth
}
//...
else if (i > 90 && i < 100) {
    //do smth
}

to something like: 像这样:

if(i > 0){
  if ( i < 5 ){

  } else if (i < 10 ) {
  //
  }
}

simpler, and leads to the same result 更简单,并导致相同的结果

A possible design pattern to consider: 可能要考虑的设计模式:

Facing a similar problem (range testing ICAO Squawk values with sparseness), I too was confounded by the rather messy look of the code. 面对类似的问题(以稀疏性对ICAO Squawk值进行范围测试),我也对代码的凌乱外观感到困惑。

My solution was a static function that performed a range test, then simply called that to compute the 'between' test (if there is a Java between function, I don't know it...). 我的解决方案是执行范围测试的静态函数,然后简单地称其为计算“中间”测试(如果函数之间存在Java,我就不知道...)。 Here are snippets of how I solved this; 以下是我如何解决此问题的摘要; not a good pattern for everything but maybe this sparks an idea for another solution. 不是所有事物的好模式,但是也许这激发了另一个解决方案的想法。

/**
 * Range test shorthand 
 * 
 * @param value - value to test
 * @param min   - lower bound
 * @param max   - upper bound
 * 
 * @return  true | false  
 */
private static boolean inRange(int value, int min, int max){
    return min <= value && value <= max;
}

This is how I used it: 这是我的用法:

        // not a pre-defined code...  run some range tests next to quess
    if (inRange(squawk,41,57)) {
        message = "test";
    }
    else if (inRange(squawk,100,400)){
        message = "Unique Purpose and Experimental activities";  // ud 17-OCT
    }
    else if (inRange(squawk,100,700)){  // Note! this is an overlap defined IN Order 7110.66E
        message = "Oceanic Airspace";   // ud 17-OCT
    }
    else if (inRange(squawk,1207,1272)){
        message = "DVFR Aircraft (USA)";  // ud 17-OCT
    }
    else if (inRange(squawk,1273,1275)){
        message = "Calibration Performance Monitoring Equipment";
    }

[...] [...]

Using simple cascading would not work for me since it's a sparse set, and there is also a map involved... (here is part of that, for reference): 使用简单的级联对我来说不起作用,因为它是稀疏的集合,并且还涉及一张地图...(这是其中的一部分,仅供参考):

private static final Map<Integer,String> codes;
static {
    codes = new HashMap<Integer,String>();

    /* unremarkable codes */
    codes.put(0,    "");
    codes.put(0000, "");  // there message defined for 0000 in 7110.66E spec, but I'm using an empty string
    codes.put(0021, "VFR below 5000ft.");
    codes.put(0022, "VFR above 5000ft.");
    codes.put(0033, "Parachute Drop Operations");
    codes.put(0100, "Airport Flight Operations");
    codes.put(0500, "External ARTCC subsets");
    codes.put(0600, "External ARTCC subsets");
    codes.put(0700, "External ARTCC subsets");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM