2016-11-29 32 views
8

我有一種情況需要檢查多個條件,其中每個組合都有不同的結果。在我的具體情況中,我有兩個變量,它們是枚舉類型,每個變量可以是2個不同的值。有效檢查多個條件

enum Enum1 
{ 
    COND_1, 
    COND_2 
} 
enum EnumA 
{ 
    COND_A, 
    COND_B 
} 
Enum1 var1; 
EnumA varA; 

這給了我4個可能的條件,這需要4個不同的結果。我想出了這樣的幾個不同的方法,或者使用if語句或switch語句:

if(var1 == Enum1.COND_1 && varA == EnumA.COND_A) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_1 && varA == EnumA.COND_B) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_A) 
{ 
    // Code 
} 
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_B) 
{ 
    // Code 
} 

或者:

switch(var1) 
{ 
    case COND_1: 
     switch(varA) 
     { 
      case COND_A: 
       // Code 
       break; 
      case COND_B: 
       // Code 
       break; 
     } 
     break; 
    case COND_2: 
     switch(varA) 
     { 
      case COND_A: 
       // Code 
       break; 
      case COND_B: 
       // Code 
       break; 
     } 
     break; 
} 

我想過別人的,但不希望填充代碼:P我想知道什麼是最好的方法來做到這一點。我認爲交換機比較容易閱讀,但ifs更短。如果交換機可能有多種條件,我認爲它會非常酷,但我還沒有聽說過。這也引出了一個問題:用任意數量的變量和可能的值做這件事的最好方法是什麼?

+2

如果你以一種嚴肅的方式來做這件事,那麼你可能會使用一個規則引擎或其他第三方系統來抽取所有的代碼。 – Kayaman

+1

爲什麼你需要檢查這樣的多個條件?你究竟在做什麼?這可以通過重新設計以更好的方式解決,而不是試圖在ifs和switch之間做出決定。 – Kayaman

+0

https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern –

回答

3

我更喜歡if變種沒有嵌套,因爲它很短,你有所有的條件在一行。

在調試過程中停止代碼時,可能會變得乏味,因爲您必須跨越所有前面的條件,即O(n)。執行代碼時,這應該不重要,因爲編譯器可能會優化代碼。

沒有明顯的最佳方式,所以你將不得不嘗試一下。

1

也許瘋狂的想法,但你可以使用標誌構造一個int或一個字節,並在一個開關中使用它。

private int getIntegerStateForConditions(boolean... conditions){ 
    int state = 0; 
    int position = 0; 
    for(boolean condition: conditions){ 
     if(condition){ 
      state = state || (1 << position++); 
     } 
    } 
    return state; 
} 

...

switch(getIntegerStateForCondition((var1 == Enum1.COND_1), (var2 == EnumA.COND_A)){ 
    case 0: ... //both condition false 
    case 1: ... //first condition true second false 
    case 2: ... //first false, second true ... 
} 

...

我覺得這是被幹淨的代碼很遠,但它看起來更好。

1

如果我是你,我會依靠位標誌,以只有一個byte(因爲你只有4使用情況)來處理和使用switch聲明此byte來管理你的所有用例。

事情是這樣的:

private static final int COND_2 = 1; 
private static final int COND_B = 2; 

private byte value; 

public void setValue(Enum1 enum1) { 
    if (enum1 == Enum1.COND_1) { 
     this.value &= ~COND_2; 
    } else { 
     this.value |= COND_2; 
    } 
} 

public void setValue(EnumA enumA) { 
    if (enumA == EnumA.COND_A) { 
     this.value &= ~COND_B; 
    } else { 
     this.value |= COND_B; 
    } 
} 

public Enum1 getEnum1() { 
    return (this.value & COND_2) == COND_2 ? Enum1.COND_2 : Enum1.COND_1; 
} 


public EnumA getEnumA() { 
    return (this.value & COND_B) == COND_B ? EnumA.COND_B : EnumA.COND_A; 
} 

然後你的測試將是:

switch (value) { 
    case 0 : 
     // 1-A; 
     break; 
    case 1 : 
     // 2-A; 
     break; 
    case 2 : 
     // 1-B; 
     break; 
    case 3 : 
     // 2-B; 
     break; 
} 
9

爲小型使用情況下,我可能會去嵌套if語句。但是如果你有很多常量,那麼使用流的模式可能會使你的代碼更易於閱讀和維護(性能損失很小)。你可以解決它使用這樣的流:

Stream.of(new Conditional(COND_1, COND_A,() -> {/* do something */}), 
      new Conditional(COND_1, COND_B,() -> {/* do something */}), 
      new Conditional(COND_2, COND_A,() -> {/* do something */}), 
      new Conditional(COND_2, COND_B,() -> {/* do something */})) 
     .filter(x -> x.test(var1, varA)) 
     .findAny() 
     .ifPresent(Conditional::run); 

這將需要一個支持類:

class Conditional implements BiPredicate<Enum1, EnumA>, Runnable 
{ 
    private final Enum1 var1; 
    private final EnumA varA; 
    private final Runnable runnable; 

    public Conditional(Enum1 var1, EnumA varA, Runnable runnable) { 
     this.var1 = var1; 
     this.varA = varA; 
     this.runnable = runnable; 
    } 

    @Override 
    public boolean test(Enum1 enum1, EnumA enumA) { 
     return var1 == enum1 && varA == enumA; 
    } 

    @Override 
    public void run() { 
     runnable.run(); 
    } 
} 
1

我個人比較喜歡這樣的:

if(understandableNameInContextName1(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName2(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName3(var1, varA)) 
{ 
    // Code 
} 
else if(understandableNameInContextName4(var1, varA)) 
{ 
    // Code 
} 

private boolean understandableNameInContextName1(Object var1, Object varA){ 
return (var1 == Enum1.COND_1 && varA == EnumA.COND_A); 
} 

private boolean understandableNameInContextName2(Object var1, Object varA){ 
return (var1 == Enum1.COND_1 && varA == EnumA.COND_B); 
} 

private boolean understandableNameInContextName3(Object var1, Object varA){ 
return (var1 == Enum1.COND_2 && varA == EnumA.COND_A); 
} 

private boolean understandableNameInContextName4(Object var1, Object varA){ 
return (var1 == Enum1.COND_2 && varA == EnumA.COND_B); 
} 

和方法的名稱可能是,isOrderShippedAndDelivered(),isRequestSendAndAckRecieved()。

原因是這會使代碼更具可讀性。 除非你有數據可以讓你回到這些if語句,否則優化這些數據不會有太大的收穫。

參見: https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil

7

性能差異可能是微不足道的在這裏,所以我會專注於急促和可讀性。所以,我只想簡化if的有點使用臨時變量:

boolean is_1 = (var1 == Enum1.COND_1); 
boolean is_A = (varA == EnumA.COND_A); 

if(is_1 && is_A) 
{ 
    // Code 
} 
else if(is_1 && !is_A) 
{ 
    // Code 
} 
else if(!is_1 && is_A) 
{ 
    // Code 
} 
else if(!is_1 && !is_A) 
{ 
    // Code 
} 
3

我肯定更喜歡平坦的版本,它可以只用一點點減少重複:

// If you can't make the variables final, make some final copies 
final Enum1 var1 = Enum1.COND_2; 
final EnumA varA = EnumA.COND_B; 

class Tester { // You could also make an anonymous BiPredicate<Enum1, EnumA> 
    boolean t(Enum1 v1, EnumA vA) { 
     return var1 == v1 && varA == vA; 
    } 
}; 

Tester tes = new Tester(); 

if (tes.t(Enum1.COND_1, EnumA.COND_A)) { 
    // code 
} else if (tes.t(Enum1.COND_1, EnumA.COND_B)) { 
    // code 
} else if (tes.t(Enum1.COND_2, EnumA.COND_A)) { 
    // code 
} else if (tes.t(Enum1.COND_2, EnumA.COND_B)) { 
    // code 
} 

運行它here。您可以使用static import of the enums來避免提及枚舉名稱,例如縮短和減少冗餘。 tes.t(COND_1, COND_B)。或者,如果您願意放棄一些編譯時安全性,則可以傳遞一個字符串,將其轉換爲兩個枚舉值,例如, tes.t("COND_1 COND_A")(實施留給讀者)。

0

類型取決於代碼的複雜性和組合的數量,但另一個選項是一個字典,其中的鍵包含枚舉的Tuple和代碼的委託值。