2008-08-08 138 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) 
     ) 
    { 
    } 

沒有什麼不妥的水平空間(換行符),垂直對齊,或明確的指導括號表達式求值,所有這些都增強了可讀性和清晰度