Najlepszy sposób na sformatowanie instrukcji if z wieloma warunkami

88

Jeśli chcesz, aby jakiś kod był wykonywany na podstawie dwóch lub więcej warunków, jaki jest najlepszy sposób sformatowania tej instrukcji if?

pierwszy przykład: -

if(ConditionOne && ConditionTwo && ConditionThree)
{
   Code to execute
}

Drugi przykład: -

if(ConditionOne)
{
   if(ConditionTwo )
   {
     if(ConditionThree)
     {
       Code to execute
     }
   }
}

co jest najłatwiejsze do zrozumienia i przeczytania, pamiętając, że każdy warunek może być długą nazwą funkcji lub czymś podobnym.

Guy Coder
źródło
Szkoda, że ​​nikt na tej stronie nie wspomina o podłańcuchu „szybko” lub „wydajność”. Tego się tutaj nauczyłem.
Prezydent Dreamspace

Odpowiedzi:

131

Wolę opcję A

bool a, b, c;

if( a && b && c )
{
   //This is neat & readable
}

Jeśli masz szczególnie długie zmienne / warunki metod, możesz je po prostu podzielić

if( VeryLongConditionMethod(a) &&
    VeryLongConditionMethod(b) &&
    VeryLongConditionMethod(c))
{
   //This is still readable
}

Jeśli są jeszcze bardziej skomplikowane, rozważałbym wykonanie metod warunkowych oddzielnie poza instrukcją if

bool aa = FirstVeryLongConditionMethod(a) && SecondVeryLongConditionMethod(a);
bool bb = FirstVeryLongConditionMethod(b) && SecondVeryLongConditionMethod(b);
bool cc = FirstVeryLongConditionMethod(c) && SecondVeryLongConditionMethod(c);

if( aa && bb && cc)
{
   //This is again neat & readable
   //although you probably need to sanity check your method names ;)
}

IMHO Jedynym powodem dla opcji „B” byłoby posiadanie oddzielnych elsefunkcji do uruchomienia dla każdego warunku.

na przykład

if( a )
{
    if( b )
    {
    }
    else
    {
        //Do Something Else B
    }
}
else
{
   //Do Something Else A
}
Eoin Campbell
źródło
Lubię to. Chociaż nie jestem wielkim fanem pomysłu na metodę, chyba że metody już istnieją i zwracają wartość logiczną.
Thomas Owens
2
Czy w drugim przykładzie nie oceniasz wszystkiego bez powodu?
Odys
Użyłbym znaku „&&” przed warunkiem. Trudno jest mieć warunki o takiej samej długości znaków, jak w przykładzie.
Darkov
28

Inne odpowiedzi wyjaśniają, dlaczego pierwsza opcja jest zwykle najlepsza. Ale jeśli masz wiele warunków, rozważ utworzenie oddzielnej funkcji (lub właściwości) sprawdzającej warunki w opcji 1. To znacznie ułatwia odczytanie kodu, przynajmniej jeśli używasz dobrych nazw metod.

if(MyChecksAreOk()) { Code to execute }

...

private bool MyChecksAreOk()
{ 
    return ConditionOne && ConditionTwo && ConditionThree;
}

Jeśli warunki zależą tylko od lokalnych zmiennych zasięgu, możesz uczynić nową funkcję statyczną i przekazać wszystko, czego potrzebujesz. Jeśli jest mieszanka, podaj lokalne rzeczy.

Torbjørn
źródło
2
Uważam, że jest to najskuteczniejsze i łatwiejsze do dodania warunków później
pbojinov
+1 na początku uniosłem brew, ale to naprawdę najlepsza odpowiedź imho. posiadanie tej wartości logicznej isOkToDoWhateverjako właściwości ma wiele sensu.
grinch
1
Ale to po prostu przenosi ten sam złożony warunek w inne miejsce, gdzie również musi być czytelny, więc wracamy do punktu wyjścia z tym. Nie chodzi tylko o ifczytelność instrukcji, ale raczej o czytelność warunków.
Robert Koritnik
@RobertKoritnik Rozumiem, co mówisz, ale nie sądzę, że wróciliśmy do punktu wyjścia, ponieważ zmniejszyliśmy złożoność, którą czytelnik musi rozważyć za jednym razem. Może spojrzeć na warunki, LUB może spojrzeć na kod, używając warunków, w których warunki mają (miejmy nadzieję) ładną nazwę. Przynajmniej często jest mi to łatwiejsze do zrozumienia, ale czasami miło jest mieć wszystkie szczegóły w jednym miejscu. Jak zawsze, to zależy.
Torbjørn
Świetna uwaga wykorzystująca dobre nazwy metod i refaktoryzację logiki.
JB Lovell
10

Pierwszy przykład jest bardziej „czytelny”.

Właściwie, moim zdaniem powinieneś używać drugiego tylko wtedy, gdy musisz dodać jakąś „inną logikę”, ale dla prostego warunkowego użyj pierwszego smaku. Jeśli martwisz się długością warunku, zawsze możesz użyć następującej składni:

if(ConditionOneThatIsTooLongAndProbablyWillUseAlmostOneLine
                 && ConditionTwoThatIsLongAsWell
                 && ConditionThreeThatAlsoIsLong) { 
     //Code to execute 
}

Powodzenia!

David Santamaria
źródło
9

Pytanie zostało zadane i jak dotąd udzielono na nie odpowiedzi, tak jakby decyzja miała być podjęta wyłącznie na podstawie „syntaktycznej”.

Powiedziałbym, że prawidłowa odpowiedź na to, jak układasz szereg warunków w ramach if, powinna również zależeć od „semantyki”. Zatem warunki powinny zostać podzielone i pogrupowane według tego, jakie rzeczy idą ze sobą „koncepcyjnie”.

Jeśli dwa testy to tak naprawdę dwie strony tej samej monety, np. if (x> 0) && (x <= 100), a następnie umieść je razem w tej samej linii. Jeśli inny warunek jest koncepcyjnie znacznie bardziej odległy, np. user.hasPermission (Admin ()), a następnie umieść go w osobnej linii

Na przykład.

if user.hasPermission(Admin()) {
   if (x >= 0) && (x < 100) {
      // do something
   }
}
interstar
źródło
7
if (   ( single conditional expression A )
    && ( single conditional expression B )
    && ( single conditional expression C )
   )
{
   opAllABC();
}
else
{
   opNoneABC();
}

Formatowanie wielu wyrażeń warunkowych w instrukcji if-else w następujący sposób:

  1. zapewnia lepszą czytelność:
    a. wszystkie binarne operacje logiczne {&&, ||} w wyrażeniu pokazanym jako pierwszy
    b. oba operandy warunkowe każdej operacji binarnej są oczywiste, ponieważ są wyrównane w pionie
    c. operacje na zagnieżdżonych wyrażeniach logicznych stają się oczywiste za pomocą wcięć, podobnie jak instrukcje zagnieżdżania wewnątrz klauzuli
  2. wymaga jawnego nawiasu (nie polega na regułach pierwszeństwa operatorów)
    a. Pozwala to uniknąć typowych błędów analizy statycznej
  3. pozwala na łatwiejsze debugowanie
    pliku. wyłącz pojedyncze testy warunkowe za pomocą tylko //
    b. ustaw punkt przerwania tuż przed lub po każdym indywidualnym teście
    ceg ...
// disable any single conditional test with just a pre-pended '//'
// set a break point before any individual test
// syntax '(1 &&' and '(0 ||' usually never creates any real code
if (   1
    && ( single conditional expression A )
    && ( single conditional expression B )
    && (   0
        || ( single conditional expression C )
        || ( single conditional expression D )
       )
   )
{
   ... ;
}

else
{
   ... ;
}
Sean
źródło
To jest moja metoda. Jedyny problem, jaki mam, to to, że nie znalazłem jeszcze narzędzia do upiększania kodu, który oferuje to jako opcję
Speed8ump
4

Drugi to klasyczny przykład wzoru Arrow Anti, więc unikałbym tego ...

Jeśli twoje warunki są zbyt długie, wyodrębnij je do metod / właściwości.

Omar Kooheji
źródło
3

Pierwsza jest łatwiejsza, ponieważ czytając ją od lewej do prawej otrzymujemy: „Jeśli coś ORAZ coś innego ORAZ coś innego TO”, co jest łatwym do zrozumienia zdaniem. Drugi przykład brzmi „Jeśli coś WTEDY jeśli coś innego WTEDY jeśli coś innego WTEDY”, co jest niezdarne.

Zastanów się również, czy chciałbyś użyć niektórych OR w swojej klauzuli - jak byś to zrobił w drugim stylu?

RB.
źródło
0

W Perlu możesz to zrobić:

{
  ( VeryLongCondition_1 ) or last;
  ( VeryLongCondition_2 ) or last;
  ( VeryLongCondition_3 ) or last;
  ( VeryLongCondition_4 ) or last;
  ( VeryLongCondition_5 ) or last;
  ( VeryLongCondition_6 ) or last;

  # Guarded code goes here
}

Jeśli którykolwiek z warunków zawiedzie, będzie kontynuowany po zablokowaniu. Jeśli definiujesz zmienne, które chcesz zachować po bloku, musisz zdefiniować je przed blokiem.

Brad Gilbert
źródło
1
To wygląda na Perlish - w pytaniu „CO robi?” sens;) Ale faktycznie jest czytelny, gdy się do tego przyzwyczaisz.
Piskvor opuścił budynek
-2

Stoję przed tym dylematem od dawna i nadal nie mogę znaleźć odpowiedniego rozwiązania. Moim zdaniem jedynym dobrym sposobem jest najpierw spróbować pozbyć się warunków, aby nie porównywać nagle 5 z nich.

Jeśli nie ma alternatywy, to tak jak sugerowali inni - podziel ją na oddzielne i skróć nazwy lub pogrupuj je, a np. Jeśli wszystko musi być prawdziwe, użyj czegoś takiego jak "jeśli nie ma fałszu w tablicy x, a następnie uruchom".

Jeśli wszystko zawiedzie, @Eoin Campbell dał całkiem niezłe pomysły.

Tiper Loc
źródło
Nie dodaje to niczego nowego do już istniejących odpowiedzi.
jerney
-4

Kiedy stan jest naprawdę złożony, używam następującego stylu (przykład z życia w PHP):

if( $format_bool &&
    (
        ( isset( $column_info['native_type'] )
            && stripos( $column_info['native_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['driver:decl_type'] )
            && stripos( $column_info['driver:decl_type'], 'bool' ) !== false
        )
        || ( isset( $column_info['pdo_type'] )
            && $column_info['pdo_type'] == PDO::PARAM_BOOL
        )
    )
)

Uważam, że jest ładniejszy i bardziej czytelny niż zagnieżdżanie wielu poziomów if() . W niektórych przypadkach, takich jak ten, nie można po prostu rozbić złożonego warunku na części, ponieważ w przeciwnym razie musiałbyś wielokrotnie powtarzać te same instrukcje w if() {...}bloku.

Uważam też, że dodanie „powietrza” do kodu jest zawsze dobrym pomysłem. Znacznie poprawia czytelność.

CodeDriller
źródło