2008-08-08 102 views
28

这是我用过的每种语言给我的bug,我有一条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) 
    { 

回答

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

确保你给你的变量名actualy表明意图,而不是功能。这将大大帮助开发者维护你的代码......它可能是你!

+0

简单,容易做的,有效的。 – 2010-05-13 11:12:20

5

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

当我有很大的条件时,我寻找原因。有时我看到我应该使用多态,有时我需要添加一些状态对象。基本上,这意味着需要重构(一种代码味道)。

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

1

我诉诸单独的布尔值:

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

if (cond1 && cond2) {} 
3

我见过很多人,编辑或者缩进各条件的if语句与一个标签,或将其与开放的括号匹配起来:

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

我通常把右括号放在同一行的最后一个条件:

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

但我不认为这是相当干净。

6

我会经常分裂这些成组件的布尔变量:

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

嗯,首先,为什么不:

如果(VAR1 & & VAR2 & & VAR2 & & VAR3 & & VAR4 & & VAR5 & & var6){
...

另外,它很难重构抽象的代码示例。如果你展示了一个具体的例子,找出一个更好的模式来解决这个问题会更容易。

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

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

这是一样的: (不一样的,见上面注!)

OK =(COND1 & & COND2 & & COND3 & & COND4 & & cond5 & & COND6) ;

+0

如果`&&`操作符短路,则不一样。 – ptomato 2010-05-10 15:57:33

+0

哇。我应该知道这一点。我的第一个facepalm在我自己的答案之一;-) – 2010-05-13 10:43:17

0

我喜欢水平,打破他们失望,所以我格式化你例子是这样的:

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)){ 
4

检查出肯特贝克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 
} 

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

我高度推荐该书的方式。

+0

'快速返回'杀死'箭头'反模式太:) – 2010-05-13 18:29:53

+0

我不同意这些卫兵更容易阅读。对“复杂”情况的评论会更好。 – 2017-12-20 22:10:17

12

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

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

+3

我不喜欢分解有条件的,因为它污染了一次性函数不可重用的代码结构。我宁愿有一个大的IF声明,对每个“组”的相关检查发表评论。 – 2010-05-09 12:03:28

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”等是而不是可接受的变量名称。他们应该有一个反映他们所代表的数据的潜在性质的名字。

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?)。如果是这样,那可能是最干净的方法。

-2

如果你这样做:

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

那么你也可以到自己是不是真实的情况作出回应。例如,如果您正在验证输入,则可以向用户提供有关如何正确格式化或等等的提示。

+3

这也许是这个问题最糟糕的解决方案。 – 2008-10-14 20:59:45

+1

水平滚动条 - 激活!!! 11eleven – 2010-05-13 18:34:19

0

麦克道尔,

你是正确的,使用单一的“&”操作时,该表达式的两边评估。但是,当使用'& &'运算符(至少在C#中)时,返回false的第一个表达式是最后一个表达式。这使得FOR语句之前的评估与其他任何方式一样好。

1

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

0

@tweakt

这是没有更好的,但我在过去所做的那样:

布尔OK = COND1; ok & = cond2; ok & = cond3; ok & = cond4; ok & = cond5; ok & = cond6;

哪相同:

OK =(COND1 & & COND2 & & COND3 & & COND4 & & cond5 & & COND6);

其实,这两样东西在大多数语言中是不一样的。第二个表达式通常会在其中一个条件为假时立即停止评估,如果评估条件非常昂贵,这可能会大大提高性能。

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

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

while(false)是一种俗气。我希望语言有一个名为“曾经”的范围操作符,或者你可以轻松地打出一些东西。

2

试试看Functors and 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); 

现在所有这些isHonorRoll谓词的细节,并用来评估他们关闭:

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

我喜欢打破每个条件为描述性的变量。

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

史蒂夫Mcconell的建议,从Code Complete: 使用多维表。每个变量都作为表的索引, ,if语句变成表查找。例如,如果(大小== 3 & &重量> 70) 转化为表项的决定[SIZE] [weight_group]

0

如果我在Perl这样做,这是我怎么可能会遇到的检查。

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

    ... # Place Code Here 
} 

如果你打算使用这种过度子程序与return取代的last每个实例;

1

在像PHP反射的语言,你可以使用可变变量:

$vars = array('var1', 'var2', ... etc.); 
foreach ($vars as $v) 
    if ($$v == true) { 
     // do something 
     break; 
    } 
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(); 
    } 

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

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

运算符优先规则很棘手,例如& &比||有更高的优先级,但|具有优先级比& &

所以,...

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

没有什么不妥的水平空间(换行符),垂直对齐,或明确的指导括号表达式求值,所有这些都增强了可读性和清晰度