2016-11-29 35 views
8

Mam sytuację, w której muszę sprawdzić wiele warunków, gdzie każda kombinacja ma inny wynik. W moim szczególnym stanie mam 2 zmienne, które są typami enum, które mogą mieć 2 różne wartości.Sprawnie sprawdź wiele warunków

Daje to 4 możliwe warunki, które wymagają 4 różnych rezultatów. Mam wymyślić kilka różnych sposobów w ten sposób, albo za pomocą instrukcji if lub oświadczeń przełącznika:

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 
} 

Lub:

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; 
} 

myślałem o innych, ale nie chcą aby wypełnić ten kod: P Chciałbym wiedzieć, jak najlepiej to zrobić. Myślę, że zmiana jest nieco łatwiejsza do odczytania, ale jeśli są krótsze. Myślę, że byłoby naprawdę fajnie, gdyby przełączniki mogły mieć wiele warunków, ale nie słyszałem o tym. To również nasuwa pytanie: jaki jest najlepszy sposób na zrobienie tego z dowolną liczbą zmiennych i możliwych wartości?

+2

Jeśli robisz to w sposób * poważny *, to prawdopodobnie użyjesz silnika reguł lub innego systemu zewnętrznego, który wyodrębni cały kod. – Kayaman

+1

Dlaczego trzeba sprawdzać wiele takich warunków? Co właściwie robisz? Może to zostać rozwiązane w lepszy sposób dzięki przeprojektowaniu, zamiast próbować decydować między ifs i switchami. – Kayaman

+0

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

Odpowiedz

3

Preferuję wariant if bez zagnieżdżania, ponieważ jest on krótki i wszystkie warunki występują w jednym wierszu.

Podczas zatrzymywania kodu podczas debugowania może stać się żmudny, ponieważ trzeba przekroczyć wszystkie poprzednie warunki, czyli O (n). Podczas wykonywania kodu nie powinno to mieć znaczenia, ponieważ prawdopodobnie kompilator zoptymalizuje kod.

Nie ma oczywistego najlepszego sposobu, więc będziesz musiał trochę poeksperymentować.

1

Może szalony pomysł, ale można skonstruować int lub bajt przy użyciu flag i używać go w jednym przełączniku.

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 ... 
} 

...

myślę, że to jest bardzo daleko od bycia czysty kod, ale wygląda lepiej.

1

Gdybym był tobą chciałbym polegać na flag bitowych, aby tylko jeden byte mieć (jak masz tylko 4 przypadki użycia) do czynienia i używać switch oświadczenie w tej byte zarządzać wszystkich przypadków użycia.

coś takiego:

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; 
} 

Zmodyfikowane testy byłoby:

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

dla małych przypadku użycia to pewnie iść do zagnieżdżonych if wypowiedzi. Ale jeśli masz wiele stałych enum, być może wzorzec wykorzystujący strumienie może sprawić, że twój kod będzie łatwiejszy do odczytania i utrzymania (za niewielką karę wykonania).Można go rozwiązać za pomocą strumienia takiego:

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); 

Wymagałoby to klasę drugoplanowy:

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

ja osobiście wolę ten:

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); 
} 

a nazwy metod może być podobne, isOrderShippedAndDelivered(), isRequestSendAndAckRęte().

Powodem jest to, że dzięki temu kod stanie się bardziej czytelny. Jeśli nie masz danych, które prowadzą z powrotem do tych instrukcji, nie ma większego wpływu na ich optymalizację.

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

7

różnice w wynikach są prawdopodobnie znikomy tutaj, więc chciałbym skupić się na duszność i czytelności. Więc chciałbym po prostu uprościć if „s trochę za pomocą zmiennych tymczasowych:

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

ja zdecydowanie wolę wersję płaski, to może po prostu użyć trochę mniej powielania:

// 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 
} 

uruchomić go here. Może być nawet krótszy i mniej zbędny, wykonując numer static import of the enums, aby uniknąć wymieniania nazw wyliczeniowych, np. tes.t(COND_1, COND_B). Lub jeśli chcesz zrezygnować z pewnego czasu kompilacji, możesz przekazać ciąg, który zostanie przekonwertowany na dwie wartości wyliczeniowe, np. tes.t("COND_1 COND_A") (implementacja jest pozostawiana czytnikowi).

0

Rodzaj zależy od złożoności kodu i liczby kombinacji, ale inną opcją jest słownik z kluczem składającym się z krotki twoich wyliczeń i wartości delegata do kodu.