你如何处理巨大的条件?

时间:2008-08-08 16:45:59

标签: language-agnostic if-statement

这是我用过的每种语言都有问题的东西,我有一个if语句,但条件部分有很多检查,我必须将它分成多行,使用嵌套的if语句或只是接受它是丑陋的继续我的生活。

你发现的其他方法对我和其他遇到同样问题的人有用吗?

示例,全部在一行:

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

示例,多行:

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

实施例嵌套的:

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

21 个答案:

答案 0 :(得分:61)

将条件分成几个布尔值,然后使用主布尔值作为条件。

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

更好的是:

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

确保您提供的变量名称表示意图而非功能。这将极大地帮助开发人员维护您的代码......可能就是您!

答案 1 :(得分:12)

我很惊讶没有人得到这个。有一个专门针对此类问题的重构:

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

答案 2 :(得分:7)

这里有两个问题需要解决:可读性和可理解性

“可读性”解决方案是一种风格问题,因此可以解释。我的偏好是:

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

或者这个:

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

也就是说,在扫描代码时,这种复杂的检查很难在精神上解析(特别是如果你不是原作者)。考虑创建一个帮助方法来抽象出一些复杂性:

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

现在,在视觉上扫描“SomeMethod”方法时,隐藏了测试逻辑的实际复杂性,但保留了语义意义,供人类在高层次上理解。如果开发人员确实需要了解详细信息,则可以检查AreAllConditionsMet方法。

我认为这正式被称为“分解条件”重构模式。像Resharper或Refactor Pro这样的工具!可以轻松做这种重构!

在所有情况下,拥有可读且易于理解的代码的关键是使用真实的变量名称。虽然我知道这是一个人为的例子,但“var1”,“var2”等是可接受的变量名。它们的名称应该反映它们所代表的数据的基本性质。

答案 3 :(得分:6)

我经常把它们分成组件布尔变量:

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

答案 4 :(得分:5)

首先,我会删除所有== true部分,这会缩短50%;)

当我情况很好时,我会寻找原因。有时我看到我应该使用多态,有时我需要添加一些状态对象。基本上,它意味着需要重构(代码气味)。

有时我使用De-Morgan's laws来简化布尔表达式。

答案 5 :(得分:4)

查看Kent Beck的Implementation Patterns。我想到的一种特殊模式可能对这种情况有所帮助......它被称为“卫兵”。你可以将它们分解成一个守护者,而不是有很多条件,这可以清楚地表明方法中的不利条件。

例如,如果你有一个方法可以做某事,但是在某些条件下它不应该做某事,而不是:

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

您可以将其更改为:

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

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

它有点冗长,但更具可读性,特别是当你开始进行奇怪的嵌套时,守卫可以提供帮助(结合提取方法)。

我强烈推荐那本书。

答案 6 :(得分:3)

我见过很多人和编辑要么在if语句中使用一个标签缩进每个条件,要么将它与开放的paren匹配:

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

我通常将关闭的paren放在与最后一个条件相同的行上:

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

但我不认为这很干净。

答案 7 :(得分:2)

嗯,首先,为什么不呢:

  

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

此外,重构抽象代码示例非常困难。如果您展示了一个特定的示例,那么识别更好的模式以适应问题会更容易。

这不是更好,但我过去做过的事情: (以下方法可以防止布尔测试短路,所有测试都会运行,即使第一个是假的。除非你知道你需要在返回之前总是执行所有代码,否则不推荐使用模式 - 感谢ptomato发现我的错误!)

  

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

与:相同(不一样,见上文注释!)

  

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

答案 8 :(得分:2)

史蒂夫麦克奈尔的建议来自Code Complete: 使用多维表。每个变量都作为表的索引, 并且if语句变成表查找。例如,如果(size == 3&amp;&amp; weight&gt; 70) 转换为表条目决定[size] [weight_group]

答案 9 :(得分:2)

尝试查看Functors和Predicates。 Apache Commons项目有一组很好的对象,允许您将条件逻辑封装到对象中。可以在O'reilly here上找到它们的使用示例。代码示例摘录:

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

现在所有这些的详细信息是HonorRoll谓词和用于评估它们的闭包:

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

答案 10 :(得分:1)

我求助于单独的布尔值:

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

if ( cond1 && cond2 ) {}

答案 11 :(得分:1)

在PHP等反射语言中,您可以使用变量:

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

答案 12 :(得分:1)

正如其他人所说,我会分析你的条件,看看是否有办法将其外包给其他方法以提高可读性。

答案 13 :(得分:0)

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

而不是

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

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

而不是

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

如果多个条件表达式不使用显式括号表示表达式分析,而不依赖于运算符优先级规则和较少的括号,则大多数用于检查代码的静态分析工具都会抱怨。

在打开/关闭括号{}的同一缩进级别进行垂直对齐,打开右括号(),带括号的条件表达式和左边的运算符是一种非常有用的做法,它极大地提高了代码的可读性和清晰度干扰可能被卡在一条线上的所有东西,没有垂直对齐,空格或括号

运营商优先级规则很棘手,例如&安培;&安培;优先级高于||,但是|优先于&amp;&amp;

所以,......

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

是一个非常简单的多条件表达式,仅供人类阅读和评估不当。

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

水平空间(换行),垂直对齐或明确括号指导表达式评估没有任何问题,所有这些都增强了可读性和清晰度

答案 14 :(得分:0)

如果我在Perl中这样做,我就可以运行检查。

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

  ... # Place Code Here
}

如果您计划在子程序上使用此项,请将last的每个实例替换为return;

答案 15 :(得分:0)

我喜欢将每个条件分解为描述性变量。

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
}

答案 16 :(得分:0)

@tweakt

  

这不是更好,但我过去做过的事情:

     

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

     

与以下内容相同:

     

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

实际上,这两件事在大多数语言中并不相同。第二个表达式通常会在其中一个条件为假时立即停止评估,如果评估条件很昂贵,这可能会大大提高性能。

为了便于阅读,我个人更喜欢Mike Stone的建议。很容易进行冗长的评论并保留能够提早出现的所有计算优势。如果您的代码组织混淆了将条件评估远离其他函数,您也可以在函数中内联相同的技术。这有点俗气,但你总是可以这样做:

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

while(false)有点俗气。我希望语言有一个名为“一次”的范围操作符或者你可以轻易打破的东西。

答案 17 :(得分:0)

道尔,

使用单曲'&amp;'时你是对的。表达式双方评估的运算符。但是,使用'&amp;&amp;'时运算符(至少在C#中)然后返回false的第一个表达式是最后一个计算的表达式。这使得在FOR语句之前的评价与其他任何方式一样好。

答案 18 :(得分:0)

如果您正在使用Python进行编程,那么在变量列表中应用内置all()函数会很简单(我在这里只使用布尔文字):

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

您的语言中是否有相应的功能(C#?Java?)。如果是这样,那可能是最干净的方法。

答案 19 :(得分:0)

我喜欢按级别分解它们,所以我将这样的格式化为:

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

当你有更多的嵌套时,这很方便(显然真实的条件比“= true”更有趣):

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

答案 20 :(得分:-2)

如果你这样做:

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

然后你也可以回答某些事情不真实的情况。例如,如果您正在验证输入,则可以向用户提供有关如何正确格式化输入的提示,或者其他任何内容。