Jak sobie radzisz z wielkimi warunkami?

To coś, co mnie wkurza w każdym języku, którego używałem, mam instrukcję if, ale część warunkowa ma tak wiele sprawdzeń, że muszę podzielić ją na wiele linii, użyć zagnieżdżonej instrukcji if lub po prostu zaakceptować, że jest brzydka i ruszyć dalej z moim życiem.

Czy są jakieś inne metody, które znalazłeś, które mogą być przydatne dla mnie i innych, którzy mają ten sam problem?

Przykład, wszystko w jednej linii:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true)
{

Przykład, wielowierszowy: {]}

if (var1 = true && var2 = true && var2 = true
 && var3 = true && var4 = true && var5 = true
 && var6 = true)
{

Przykład-zagnieżdżony:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
Author: BalusC, 2008-08-08

21 answers

Oddziel warunek w kilku elementach logicznych, a następnie użyj głównego elementu logicznego jako warunku.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

Jeszcze lepiej:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

Upewnij się, że podasz nazwę zmiennych, która faktycznie wskazuje na intencję, a nie funkcję. Pomoże to programiście w utrzymaniu Twojego kodu... to możesz być ty!

 61
Author: Coincoin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 16:58:43

Dziwię się, że nikt jeszcze tego nie dostał. Jest refaktoring specjalnie dla tego typu problemów:

Http://www.refactoring.com/catalog/decomposeConditional.html

 12
Author: Karl Seguin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 18:16:20

Istnieją dwie kwestie do rozwiązania tutaj: czytelność i zrozumiałość

Rozwiązanie "czytelności" jest kwestią stylu i jako takie jest otwarte na interpretację. Moje preferencje są takie:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

Lub to:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

To powiedziawszy, tego rodzaju skomplikowane sprawdzenie może być dość trudne do umysłowego przeanalizowania podczas skanowania kodu (zwłaszcza jeśli nie jesteś oryginalnym autorem). Rozważ stworzenie metody pomocniczej, aby wyodrębnić część złożoności z dala:

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

Teraz, gdy skanując wizualnie metodą" SomeMethod", rzeczywista złożoność logiki testu jest ukryta, ale znaczenie semantyczne jest zachowane dla ludzi do zrozumienia na wysokim poziomie. Jeśli deweloper naprawdę musi zrozumieć szczegóły, można zbadać Areallonditionsmet method.

Jest to formalnie znany jako "rozkład warunkowy" wzór refaktoryzacji, jak sądzę. Narzędzia takie jak Resharper lub Refactor Pro! czy wykonywanie tego rodzaju refaktoryzacji jest łatwe!

We wszystkich przypadkach klucz do posiadanie czytelnego i zrozumiałego kodu to używanie realistycznych nazw zmiennych. Chociaż rozumiem, że jest to wymyślony przykład, "var1", "var2" itp. są , a nie akceptowalnymi nazwami zmiennych. Powinny one mieć nazwę odzwierciedlającą podstawowy charakter danych, które reprezentują.

 7
Author: Simon Gillbee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:13:40

Często dzielę je na zmienne logiczne komponentu:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...
 6
Author: Mike Powell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 16:50:57

Najpierw usunąłbym wszystkie == true części, dzięki czemu będzie o 50% krótszy ;)

Kiedy mam duży stan Szukam powodów. Czasami widzę, że powinienem użyć polimorfizmu, czasami muszę dodać jakiś obiekt stanu. Zasadniczo oznacza to konieczność refaktoryzacji (zapachu kodu).

Czasami używam praw De-Morgana , aby nieco uprościć wyrażenia logiczne.

 5
Author: abyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-25 12:09:49

Zobacz Schematy implementacji autorstwa Kenta Becka. Myślę o pewnym wzorze, który może pomóc w tej sytuacji... nazywa się to "strażnikami". Zamiast mieć mnóstwo warunków, można je rozbić na straży, co wyjaśnia, które są niekorzystne warunki w metodzie.

Więc na przykład, jeśli masz metodę, która coś robi, ale są pewne warunki, w których nie powinna coś zrobić, a nie:

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

You could change do:

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

Jest nieco bardziej wyrazisty, ale o wiele bardziej czytelny, zwłaszcza gdy zaczynasz mieć dziwne zagnieżdżanie, strażnik może pomóc (w połączeniu z metodami ekstrakcji).

Przy okazji Gorąco polecam tę książkę.
 4
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:04:33

Widziałem wiele osób i edytorów albo wcięcia każdego warunku w Twojej deklaracji if z jednej zakładki, lub dopasowanie go do otwartego paren:

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

Zwykle umieszczam close paren na tej samej linii co ostatni warunek:

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}
Ale nie sądzę, żeby to było aż tak czyste.
 3
Author: pix0r,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 16:50:23

Porady Steve ' a Mcconella, z Code Complete : Użyj tabeli wielowymiarowej. Każda zmienna służy jako indeks do tabeli, i instrukcja if zamienia się w wyszukiwanie tabeli. Na przykład if (size = = 3 & & weight > 70) przekłada się na decyzję o wejściu do tabeli[size] [weight_group]

 2
Author: Yuval F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-18 05:39:28

Spróbuj spojrzeć na funktory i predykaty. Projekt Apache Commons ma świetny zestaw obiektów, które pozwalają na wbudowanie logiki warunkowej w obiekty. Przykład ich użycia jest dostępny na stronie O ' Reilly TUTAJ . Przykładowy fragment kodu:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

Teraz szczegóły wszystkich tych predykatów isHonorRoll i zamknięć używanych do ich oceny:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};
 2
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-14 17:07:27

Cóż, po pierwsze, dlaczego nie:

If (var1 && var2 & & var2 & & var3 & & var4 && var5 & & var6) {
...

Ponadto bardzo trudno jest refaktorować abstrakcyjne przykłady kodu. Jeśli pokazałeś konkretny przykład, łatwiej byłoby zidentyfikować lepszy wzór, aby dopasować się do problemu.

Nie jest lepiej, ale to, co robiłem w przeszłości: (Poniższa metoda zapobiega zwarciom Boolean testing, wszystkie testy są uruchamiane, nawet jeśli pierwszy jest fałszywy. Nie zalecany wzór chyba, że wiesz, że musisz zawsze wykonać cały kod przed powrotem -- dzięki ptomato za Zauważenie mojego błędu!)

Boolean ok = cond1;
ok & = cond2;
ok & = cond3;
ok & = cond4;
ok & = cond5;
ok & = cond6;

co jest takie samo jak: (nie to samo, patrz wyżej Uwaga!)

Ok = (cond1 & & cond2 & & cond3 & & cond4 & & cond5 & & cond6);

 2
Author: Mark Renouf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 10:45:25

Uciekam się do oddzielania wartości logicznych:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}
 1
Author: TimM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 16:49:54

Jak już wspomnieli inni, przeanalizowałbym twoje warunki, aby sprawdzić, czy istnieje sposób, w jaki możesz je zlecić innym metodom w celu zwiększenia czytelności.

 1
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:33:46

W takich językach jak PHP można używać zmiennych-zmiennych:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }
 1
Author: Milan Babuškov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-09 12:01:29

Lubię je rozkładać według poziomu, więc sformatowałbym przykład tak:

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

Jest to przydatne, gdy masz więcej zagnieżdżania, jak to (oczywiście rzeczywiste warunki byłyby bardziej interesujące niż "= true" dla wszystkiego):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){
 0
Author: Nicholas Trandem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 16:59:56

Jeśli przypadkiem programujesz w Pythonie, jest to cinch z wbudowaną funkcją all() zastosowaną nad listą Twoich zmiennych (użyję tutaj literałów logicznych):

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

Czy w Twoim języku (C#? Java?). Jeśli tak, to prawdopodobnie najczystsze podejście.

 0
Author: yukondude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:11:23

Masz rację, że używając pojedynczego operatora'&', który obie strony wyrażenia oceniają. Jednak w przypadku użycia operatora '& & ' (przynajmniej w C#), pierwsze wyrażenie zwracające false jest ostatnim wyrażeniem, które zostało ocenione. To sprawia, że umieszczenie ewaulacji przed deklaracją FOR jest tak samo dobre, jak każdy inny sposób jej wykonania.

 0
Author: TimM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:29:52

@tweakt

Nie jest lepiej, ale to co robiłem w przeszłości:

Boolean ok = cond1; ok & = cond2; ok & = cond3; ok & = cond4; ok & = cond5; ok & = cond6;

Czyli to samo co:

Ok = (cond1 & & cond2 & & cond3 & & cond4 & & cond5 & & cond6);

Właściwie, te dwie rzeczy nie są takie same w większości języków. Drugie wyrażenie Zwykle przestaje być oceniane, gdy tylko jeden z warunków false, co może być dużą poprawą wydajności, Jeśli ocena warunków jest kosztowna.

Dla czytelności, osobiście wolę propozycję Mike 'a Stone' A powyżej. Jest to łatwe do słownego komentowania i zachowuje wszystkie zalety obliczeniowe bycia w stanie wcześnie. Możesz również wykonać tę samą technikę inline w funkcji, jeśli pomyliłoby to organizację kodu, aby przenieść ocenę warunkową daleko od innej funkcji. To trochę tandetne, ale zawsze można zrobić coś w stylu:

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);
While (false) jest trochę tandetne. Chciałbym, aby języki miały Operator zasięgu o nazwie " raz " lub coś, z czego można łatwo się wyrwać.
 0
Author: fastcall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:37:12

Gdybym robił to w Perlu, w ten sposób mógłbym przeprowadzać kontrole.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

Jeśli planujesz użyć tego w podprogramie zastąp każdą instancję last na return;

 0
Author: Brad Gilbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-14 21:07:38

Lubię dzielić każdy warunek na zmienne opisowe.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
 0
Author: wusher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-13 18:15:00
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

W przeciwieństwie do

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

I

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

W przeciwieństwie do

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

Większość narzędzi analizy statycznej do badania kodu będzie narzekać, jeśli wiele wyrażeń warunkowych nie używa wyraźnego nawiasu dyktującego analizę wyrażeń, zamiast polegać na regułach pierwszeństwa operatorów i mniejszej liczbie nawiasów.

Wyrównanie pionowe na tym samym poziomie wcięć klamry open / close {}, Open close nawias (), wyrażenia warunkowe z nawiasem i operatory po lewej stronie są bardzo użyteczną praktyką, która znacznie poprawia czytelność i klarowność kodu, w przeciwieństwie do zagłuszania wszystkiego, co może być zablokowane na jednej linii, bez wyrównania pionowego, spacji lub nawiasu

Reguły pierwszeństwa operatorów są trudne, np. & & ma wyższy priorytet niż//, ale / ma pierwszeństwo niż & &

Więc, ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

Jest naprawdę łatwym wielokrotnym wyrażeniem warunkowym dla zwykłych ludzi do odczytu i oceny niewłaściwie.

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

Nie ma nic złego w poziomej przestrzeni( liniowości), wyrównaniu pionowym lub jednoznacznej ocenie wyrażeń prowadzących nawias, które zwiększają czytelność i klarowność

 0
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-03 17:11:48

Jeśli to zrobisz:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

Wtedy możesz również reagować na przypadki, w których coś nie jest prawdą. Na przykład, jeśli walidujesz dane wejściowe, możesz dać użytkownikowi wskazówkę, jak je poprawnie sformatować lub cokolwiek innego.

 -2
Author: Ryan Fox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-08 17:13:49