Frage

Das stört mich in jeder Sprache, die ich verwendet habe. Ich habe eine if-Anweisung, aber der bedingte Teil hat so viele Prüfungen, dass ich sie auf mehrere Zeilen aufteilen, eine verschachtelte if-Anweisung verwenden oder einfach akzeptieren muss, dass sie hässlich ist, und weitermachen muss mit meinem Leben.

Gibt es andere Methoden, die Sie gefunden haben und die für mich und alle anderen, die das gleiche Problem haben, von Nutzen sein könnten?

Beispiel, alles in einer Zeile:

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

Beispiel, mehrzeilig:

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

Beispielverschachtelt:

if (var1 = true && var2 = true && var2 = true && var3 = true)
{
     if (var4 = true && var5 = true && var6 = true)
     {
War es hilfreich?

Lösung

Teilen Sie die Bedingung in mehrere boolesche Werte auf und verwenden Sie dann einen Master-booleschen Wert als Bedingung.

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

Besser noch:

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

Stellen Sie sicher, dass Sie Ihren Variablen Namen geben, die tatsächlich die Absicht und nicht die Funktion angeben.Dies wird dem Entwickler bei der Pflege Ihres Codes sehr helfen ...das könntest Du sein!

Andere Tipps

Ich bin überrascht, dass noch niemand dieses bekommen hat.Es gibt ein Refactoring speziell für diese Art von Problem:

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

Hier sind zwei Probleme zu lösen:Lesbarkeit und Verständlichkeit

Die Lösung „Lesbarkeit“ ist eine Stilfrage und kann daher interpretiert werden.Meine Präferenz ist folgende:

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

oder dieses:

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

Allerdings kann es ziemlich schwierig sein, diese Art komplexer Prüfung beim Scannen des Codes mental zu analysieren (insbesondere, wenn Sie nicht der ursprüngliche Autor sind).Erwägen Sie die Erstellung einer Hilfsmethode, um einen Teil der Komplexität zu abstrahieren:

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

Beim visuellen Scannen der Methode „SomeMethod“ wird nun die tatsächliche Komplexität der Testlogik ausgeblendet, die semantische Bedeutung bleibt jedoch erhalten, damit der Mensch sie auf hoher Ebene verstehen kann.Wenn der Entwickler die Details wirklich verstehen muss, kann die Methode AreAllConditionsMet untersucht werden.

Meiner Meinung nach ist dies offiziell als Refactoring-Muster „Decompose Conditional“ bekannt.Tools wie Resharper oder Refactor Pro!kann diese Art von Refactoring einfach machen!

In allen Fällen liegt der Schlüssel zu lesbarem und verständlichem Code in der Verwendung realistischer Variablennamen.Ich verstehe zwar, dass dies ein erfundenes Beispiel ist, aber „var1“, „var2“ usw. sind es nicht akzeptable Variablennamen.Sie sollten einen Namen haben, der die zugrunde liegende Natur der von ihnen dargestellten Daten widerspiegelt.

Ich teile diese oft in boolesche Komponentenvariablen auf:

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

Zuerst würde ich alles entfernen == true Teile, das würde es 50 % kürzer machen ;)

Wenn ich schwere Beschwerden habe, suche ich nach den Gründen.Manchmal sehe ich, dass ich Polymorphismus verwenden sollte, manchmal muss ich ein Zustandsobjekt hinzufügen.Im Grunde bedeutet dies, dass eine Umgestaltung erforderlich ist (ein Code-Geruch).

Manchmal benutze ich De-Morgans Gesetze um boolesche Ausdrücke etwas zu vereinfachen.

Kasse Implementierungsmuster von Kent Beck.Ich denke an ein bestimmtes Muster, das in dieser Situation hilfreich sein könnte ...es heißt „Wächter“.Anstatt Unmengen von Bedingungen zu haben, können Sie sie in eine Wache aufteilen, die deutlich macht, welche ungünstigen Bedingungen in einer Methode sind.

Wenn Sie beispielsweise eine Methode haben, die etwas tut, es aber bestimmte Bedingungen gibt, unter denen sie nichts tun sollte, anstatt:

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

Sie könnten es ändern in:

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

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

Es ist etwas ausführlicher, aber viel besser lesbar, insbesondere wenn seltsame Verschachtelungen auftreten. Der Wächter kann helfen (in Kombination mit Extraktionsmethoden).

Ich kann das Buch übrigens wärmstens empfehlen.

Ich habe viele Leute und Redakteure gesehen, die jede Bedingung in Ihrer if-Anweisung entweder mit einem Tabulator eingerückt oder sie mit dem offenen Klammerpaar abgeglichen haben:

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

Normalerweise setze ich das Schlussparen in die gleiche Zeile wie die letzte Bedingung:

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

Aber ich glaube nicht, dass das hier ganz so sauber ist.

Steve McConnells Rat, von Code abgeschlossen:Verwenden Sie eine mehrdimensionale Tabelle.Jede Variable dient als Index für die Tabelle, und die IF -Anweisung verwandelt sich in eine Tabelle.Zum Beispiel if (size == 3 && weil> 70) übersetzt sich in die Tischeintragsentscheidung [Größe] [Weight_Group

Schauen Sie sich Funktoren und Prädikate an.Das Apache Commons-Projekt verfügt über eine große Menge an Objekten, mit denen Sie bedingte Logik in Objekte kapseln können.Ein Beispiel für ihre Verwendung finden Sie auf O'reilly Hier.Auszug aus dem Codebeispiel:

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

Nun die Details all dieser isHonorRoll-Prädikate und die Abschlüsse, die zu ihrer Auswertung verwendet werden:

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

Nun, zunächst einmal, warum nicht:

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

Außerdem ist es sehr schwierig, abstrakte Codebeispiele umzugestalten.Wenn Sie ein konkretes Beispiel zeigen würden, wäre es einfacher, ein besseres Muster zu identifizieren, das zum Problem passt.

Es ist nicht besser, aber was ich in der Vergangenheit getan habe:(Die folgende Methode verhindert, dass boolesche Tests kurzgeschlossen werden. Alle Tests werden ausgeführt, auch wenn der erste falsch ist.Kein empfohlenes Muster, es sei denn, Sie wissen, dass Sie immer den gesamten Code ausführen müssen, bevor Sie zurückkehren – Vielen Dank an ptomato, der meinen Fehler entdeckt hat!)

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

Das ist dasselbe wie: (nicht dasselbe, siehe Hinweis oben!)

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

Ich greife auf separate boolesche Werte zurück:

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

if ( cond1 && cond2 ) {}

Wie andere bereits erwähnt haben, würde ich Ihre Bedingungen analysieren, um zu sehen, ob es eine Möglichkeit gibt, sie auf andere Methoden auszulagern, um die Lesbarkeit zu verbessern.

In reflektierenden Sprachen wie PHP können Sie Variablen-Variablen verwenden:

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

Ich unterteile sie gerne nach Level, deshalb würde ich Ihr Beispiel wie folgt formatieren:

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

Dies ist praktisch, wenn Sie mehr Verschachtelungen haben, wie zum Beispiel diese (natürlich wären die tatsächlichen Bedingungen interessanter als „= true“ für alles):

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

Wenn Sie zufällig in Python programmieren, ist dies mit der integrierten Funktion ein Kinderspiel all() Funktion, die auf die Liste Ihrer Variablen angewendet wird (ich verwende hier nur boolesche Literale):

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

Gibt es eine entsprechende Funktion in Ihrer Sprache (C#?Java?).Wenn ja, ist das wahrscheinlich der sauberste Ansatz.

McDowell,

Sie haben Recht, dass bei Verwendung des einzelnen Operators „&“ beide Seiten des Ausdrucks ausgewertet werden.Wenn Sie jedoch den Operator „&&“ verwenden (zumindest in C#), ist der erste Ausdruck, der „false“ zurückgibt, der zuletzt ausgewertete Ausdruck.Dadurch ist es genauso gut, die Auswertung vor die FOR-Anweisung zu stellen, wie jede andere Vorgehensweise.

@tweakt

Es ist nicht besser, aber was ich in der Vergangenheit getan habe:

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

Das ist dasselbe wie:

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

Tatsächlich sind diese beiden Dinge in den meisten Sprachen nicht dasselbe.Der zweite Ausdruck wird normalerweise nicht mehr ausgewertet, sobald eine der Bedingungen falsch ist. Dies kann eine große Leistungsverbesserung bedeuten, wenn die Auswertung der Bedingungen teuer ist.

Aus Gründen der Lesbarkeit bevorzuge ich persönlich den obigen Vorschlag von Mike Stone.Es ist einfach, ausführliche Kommentare abzugeben, und alle rechnerischen Vorteile einer frühzeitigen Kommentierung bleiben erhalten.Sie können die gleiche Technik auch inline in einer Funktion anwenden, wenn es die Organisation Ihres Codes durcheinander bringen würde, die bedingte Auswertung weit von Ihrer anderen Funktion zu entfernen.Es ist ein bisschen kitschig, aber Sie können immer etwas tun wie:

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

das while (falsch) ist irgendwie kitschig.Ich wünschte, Sprachen hätten einen Bereichsoperator namens „once“ oder etwas, aus dem man leicht ausbrechen könnte.

Wenn ich es in Perl machen würde, könnte ich die Prüfungen so durchführen.

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

  ... # Place Code Here
}

Wenn Sie vorhaben, dies über eine Unterroutine zu verwenden, ersetzen Sie jede Instanz von last mit return;

Ich unterteile jede Bedingung gerne in beschreibende Variablen.

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
}
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

im Gegensatz zu

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

Und

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

im Gegensatz zu

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

Die meisten statischen Analysetools zum Untersuchen von Code beschweren sich, wenn mehrere bedingte Ausdrücke keine expliziten Klammern verwenden, die die Ausdrucksanalyse vorschreiben, anstatt sich auf Operatorvorrangregeln und weniger Klammern zu verlassen.

Die vertikale Ausrichtung auf der gleichen Einzugsebene von öffnenden/schließenden Klammern {}, öffnenden schließenden Klammern () und bedingten Ausdrücken mit Klammern und Operatoren auf der linken Seite ist eine sehr nützliche Vorgehensweise, die die Lesbarkeit und Klarheit des Codes erheblich verbessert, anstatt alles zu blockieren das kann möglicherweise auf eine einzelne Zeile eingeklemmt werden, ohne vertikale Ausrichtung, Leerzeichen oder Klammern

Regeln zur Vorrangstellung von Operatoren sind knifflig, z.B.&& hat höhere Vorrang als ||, aber | hat Vorrang als &&

Also, ...

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

ist ein wirklich einfacher multipler bedingter Ausdruck, den normale Menschen falsch lesen und auswerten können.

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

An horizontalem Abstand (Zeilenvorschübe), vertikaler Ausrichtung oder expliziten Klammern als Orientierungshilfe für die Ausdrucksauswertung ist nichts auszusetzen, was die Lesbarkeit und Klarheit verbessert

Wenn du das tust:

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

Dann können Sie auch auf Fälle reagieren, in denen etwas nicht stimmt.Wenn Sie beispielsweise Eingaben validieren, können Sie dem Benutzer einen Tipp geben, wie er sie richtig formatiert oder was auch immer.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top