Cum te descurci cu condiții imense?

Este ceva ce ma deranjeaza in toate limbile pe care le-am folosit, am o declaratie if, dar partea conditionala are atat de multe verificari ca trebuie sa o imparti pe mai multe linii, sa folosesc o declaratie imbricata daca accepta ca e urata si sa mearga mai departe cu viata mea.

Există alte metode pe care le-ați găsit că ar putea fi de folos pentru mine și oricine altcineva care a lovit aceeași problemă?

Exemplu, toate pe o singură linie:

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

Exemplu, multi-linie:

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

Exemplu-imbricată:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
0
fr hi bn

21 răspunsuri

Recurg la valori booleene separate:

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

if ( cond1 && cond2 ) {}
0
adăugat

McDowell,

You are correct that when using the single '&' operator that both sides of the expression evaluate. However, when using the '&&' operator (at least in C#) then the first expression to return false is the last expression evaluated. This makes putting the evaulation before the FOR statement just as good as any other way of doing it.

0
adăugat

După cum au menționat și alții, aș analiza condiționalitățile dvs. pentru a vedea dacă există o modalitate prin care puteți să o externalizați altor metode pentru a crește lizibilitatea.

0
adăugat

Am văzut o mulțime de oameni și editori, fie indentarea fiecărei condiții în declarația dvs. cu o singură filă, sau potrivirea cu parenka deschisă:

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

De obicei, pun parenul apropiat pe aceeași linie cu ultima condiție:

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

Dar nu cred că este așa de curat.

0
adăugat

Consultați Modele de implementare de Kent Beck. Există un model anume pe care mă gândesc că ar putea ajuta în această situație ... se numește "Gărzi". Mai degrabă decât să aveți o mulțime de condiții, le puteți distruge într-un gardian, ceea ce clarifică care sunt condițiile nefavorabile ale unei metode.

De exemplu, dacă aveți o metodă care face ceva, dar există anumite condiții în care nu ar trebui să facă ceva, mai degrabă decât:

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

Ați putea să o schimbați la:

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

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

Este un pic mai verbose, dar mult mai ușor de citit, mai ales când începeți să aveți cuiburi ciudate, paznicul vă poate ajuta (combinat cu metode de extragere).

I recomand foarte mult acea carte apropo.

0
adăugat
"Returnare rapidă" ucide și "anti-șablon" de șoarece
adăugat autor Arnis Lapsa, sursa
Nu sunt de acord că aceste Gărzi facilitează citirea. Un comentariu despre starea "complicată" ar fi mai bine.
adăugat autor Randy Stegbauer, sursa

@tweakt

It's no better, but what I've done in the past:

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

Which is the same as:

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

De fapt, aceste două lucruri nu sunt aceleași în majoritatea limbilor. A doua expresie va înceta de obicei să fie evaluată imediat ce una dintre condiții este falsă, ceea ce poate fi o îmbunătățire a performanței mari dacă evaluarea condițiilor este costisitoare.

Pentru lizibilitate, eu personal prefer propunerea lui Mike Stone de mai sus. Este ușor să comenteze și să păstreze toate avantajele computaționale ale capacității de a ieși devreme. De asemenea, puteți face aceeași tehnică în linie într-o funcție dacă ar confunda organizarea codului dvs. pentru a muta evaluarea condițională departe de cealaltă funcție. Este un fel de brânză, dar puteți face întotdeauna ceva de genul:

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

în timp ce (fals) este un fel de brânză. Îmi doresc ca limbile să aibă un operator de definire a domeniului numit "o singură dată" sau ceva de care să poți ieși ușor.

0
adăugat
Aceasta nu este mai bună decât codul original.
adăugat autor Randy Stegbauer, sursa

Dacă faceți acest lucru:

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

Apoi puteți să răspundeți și cazurilor în care ceva nu este adevărat. De exemplu, dacă validați datele de intrare, puteți oferi utilizatorului un sfat pentru modul în care să-l formatați corespunzător sau altceva.

0
adăugat
Bară de derulare orizontală - activați!
adăugat autor Arnis Lapsa, sursa
Aceasta este probabil cea mai gravă soluție la această întrebare.
adăugat autor Brad Gilbert, sursa

Voi descompune adesea aceste variabile booleene în componente:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...
0
adăugat

Separați starea în mai multe booleani și apoi folosiți booleanul principal ca o condiție.

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)
{
    // ...
}

Mai bine:

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)
     {
         // ...
     }
}

Asigurați-vă că dați numele variabilelor care indică mai degrabă intenția decât funcția. Acest lucru va ajuta foarte mult dezvoltatorul menținerea codului dvs. ... ar putea fi TU!

0
adăugat
Simplu, ușor de înțeles și eficient.
adăugat autor Graham Clark, sursa

Sunt surprins că nimeni nu a primit încă unul. Există un refactorizant special pentru acest tip de problemă:

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

0
adăugat
Nu-mi place să descompun condițional, deoarece poluează structura codului cu funcții unice care nu sunt reutilizabile. Aș prefera să am o declarație IF cu comentarii pentru fiecare "grup" de cecuri aferente.
adăugat autor Milan Babuškov, sursa

În primul rând, aș elimina toate părțile == true , ceea ce ar face-o cu 50% mai scurtă;)

Când am o stare mare, caut motivele. Uneori văd că ar trebui să folosesc polimorfismul, uneori trebuie să adaug un obiect de stat. Practic, aceasta implică o refactorizare este necesară (un miros de cod).

Uneori folosesc legile lui De-Morgan pentru a simplifica puțin expresiile booleene.

0
adăugat

Dacă se întâmplă să fiți programat în Python, este un cinch cu funcția all() încorporată aplicată în lista variabilelor dvs. (Voi folosi doar literali booleeni aici):

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

Există o funcție corespunzătoare în limba dvs. (C #? Java?). Dacă este așa, este probabil cea mai curată abordare.

0
adăugat

În primul rând, de ce nu:

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

De asemenea, este foarte greu să refaceți exemple de cod abstract. Dacă ați arătat un exemplu specific, ar fi mai ușor să identificați un model mai bun pentru a se potrivi cu problema.

Nu este mai bine, dar ceea ce am făcut în trecut: (Următoarea metodă previne testarea booleană la scurtcircuit, toate testele sunt executate chiar dacă prima este falsă. Nu este un model recomandat decât dacă știi că trebuie să executați întotdeauna tot codul înainte de a reveni - Vă mulțumim pentru că am văzut greșeala mea)

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

Which is the same as: (not the same, see above note!)

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

0
adăugat
Wow. Ar fi trebuit să știu asta. Prima mea față-verso la unul dintre propriile mele răspunsuri ;-)
adăugat autor Mark Renouf, sursa
Nu este același lucru dacă operatorul && este scurtcircuitat.
adăugat autor ptomato, sursa

Există două aspecte care trebuie abordate aici: lizibilitatea și înțelegerea

Soluția "de citire" este o problemă de stil și, ca atare, este deschisă interpretării. Preferința mea este următoarea:

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

sau asta:

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

Acestea fiind spuse, acest tip de verificare complexă poate fi destul de greu de analizat în mod mental în timpul scanării codului (mai ales dacă nu sunteți autorul original). Luați în considerare crearea unei metode de ajutor pentru a elimina o parte din complexitate:

/// 
/// Tests whether all the conditions are appropriately met
/// 
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
    }
}

Acum, când scanăm vizual metoda "SomeMethod", complexitatea reală a logicii de testare este ascunsă, însă sensul semantic este păstrat pentru ca oamenii să înțeleagă la un nivel înalt. Dacă dezvoltatorul are nevoie să înțeleagă detaliile, este posibil să se examineze metoda AreAllConditionsMet.

Acest lucru este cunoscut în mod formal ca modelul de refacere "Descompune condițional", cred. Instrumente precum Resharper sau Refactor Pro! poate face acest fel de refactoring ușor!

În toate cazurile, cheia pentru a avea un cod lizibil și ușor de înțeles este să utilizați nume de variabile realiste. În timp ce înțeleg acest lucru este un exemplu conturat, "var1", "var2", etc sunt nu nume de variabile acceptabile. Acestea ar trebui să aibă un nume care să reflecte natura fundamentală a datelor pe care le reprezintă.

0
adăugat

În limbile de reflecție precum PHP, puteți utiliza variabile variabile:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }
0
adăugat

Steve Mcconell's advice, from Code Complete: Use a multi-dimensional table. Each variable serves as an index to the table, and the if statement turns into a table lookup. For example if (size == 3 && weight > 70) translates into the table entry decision[size][weight_group]

0
adăugat

Dacă aș fi făcut-o în Perl, așa aș putea rula cecurile.

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

  ... # Place Code Here
}

Dacă intenționați să utilizați acest lucru pe o subrutină, înlocuiți fiecare instanță de last cu return ;

0
adăugat

Îmi place să rup fiecare condiție în variabile descriptive.

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
adăugat
Care este scopul creării de răni individuale. De asemenea, comparăți fiecare varX cu true și asociați-o cu isVarXValid bool. isVar1Valid = var , care este redundant. Aveți deja rădăcini pentru a începe, de ce nu doar dacă (var1 && var2 && var3 && var4)
adăugat autor dreamlax, sursa
@dreamlax Ai dreptate. Am folosit un exemplu prost.
adăugat autor wusher, sursa

Încearcă să te uiți la funcții și predicții. Proiectul Apache Commons are un set mare de obiecte care vă permit să încapsulați logica condiționată în obiecte. Exemplu de utilizare a acestora este disponibil pe site-ul O'reilly aici . Extras din exemplul de cod:

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

Acum, detaliile tuturor acestor predicate suntHonorRoll și închiderile utilizate pentru a le evalua:

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 );
  }
};
0
adăugat
Frumos! Unul pentru a descărca și a juca cu metinks.
adăugat autor toolkit, sursa

Îmi place să-i descompun pe nivel, așa că ți-aș forma un exemplu așa:

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

Este util când ai mai multe cuiburi, așa (evident că condițiile reale ar fi mai interesante decât "= adevărat" pentru tot):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){
0
adăugat
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

spre deosebire de

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

spre deosebire de

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

Most of the static analysis tools for examining code will complain if multiple conditional expressions do not use explicit parenthesis dictating expression analysis, instead of relying on operator precedence rules și fewer parenthesis.

Vertical alignment at the same indent level of open/close braces {}, open close parenthesis (), conditional expressions with parenthesis și operators on the left is an very useful practice, which greatly ENHANCES readability și clarity of the code spre deosebire de jamming everything that can possibly be jammed onto a single line, sans vertical alignment, spaces or parenthesis

Operator precedence rules are tricky, e.g. && has higher precedence than ||, but | has precedence than &&

Asa de, ...

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

is a really easy multiple conditional expression for mere humans to read și evaluate improperly.

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

There is nothing wrong with horizontal space (linefeeds), vertical alignment, or explicit parenthesis guiding expression evaluation, all of which ENHANCES readability și clarity

0
adăugat