2010-03-06 87 views
8

多年的編碼帶來我相信,努力實現這種如果有條件編碼:(C語言表現出來,但它的相關的幾乎任何語言)如果條件最好的編碼習慣是什麼?

if(a <= 0) 
     return false; 
if(strlen(str) <= a) 
     return false; 
if(str[a] == NULL) 
     return false; 
return true; 

我認爲這是比更可讀接下來的編碼樣本,尤其是在廣大的條件:

if(a >0) 
{ 
    if(strlen(str) > a) 
    { 
      if(str[a] != NULL) 
      { 
       return true; 
      } 
     } 
    } 

而上的代碼是更具可讀性,大狀況下,你可能會發現自己關閉如此多的「}」,最後的代碼,我認爲這是編譯成性能更好的代碼。

你認爲哪一個最好用?

(在這個例子的代碼只是爲了證明不採取它的字面)

+1

第二個將是可讀格式。你可以在&&條件中有這三個表達式。所以,你會只使用一個條件。 – Pavunkumar 2010-03-06 06:12:15

+1

也許我的編碼樣本不是這個問題的「正確」樣本。我知道你可以把它放在一條線上。但我試圖詢問關於編碼習慣本身。 – aviv 2010-03-06 07:15:10

回答

2

如果您遵循Design By Contract設計,那麼您在輸入功能時必須滿足前提條件。雖然在某些情況下可以證明某些先決條件已經滿足,但在這些情況下,測試不是必需的,我們假設在這裏測試是必要的。在這些假設下,功能合同式的前提條件必須在其他任何事情都可以完成之前進行檢查,並且檢查應該被認爲與功能應該做的實際工作無關。那麼它遵循:

  • 試驗的先決條件必須在任何其他代碼的功能
  • 如果先決條件測試失敗,函數應立即返回

返回出現立即確保條件測試與功能的實際主體完全分離,不僅在語義上而且在詞彙上都是如此。這樣可以輕鬆查看代碼並確定是否遵循功能合同。它還使得在功能合同被修改的情況下稍後添加或刪除先決條件變得容易。

示例代碼:

// function contract 
// 
// pre-conditions: 
// 
// o bar must not be zero 
// o foo_ptr must not be NULL 
// o foo_ptr must refer to a foo variant of type blue_foo 
// 
// ... 

Foo *foo_blue_init_with_bar(Foo *foo_ptr, int bar, foo_status *status) { 

    // ***** Test Pre-conditions ***** 

    // bar must not be zero 
    if (bar == 0) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_BAR; 
     return NULL; 
    } 

    // foo_ptr must not be NULL 
    if (foo_ptr == NULL) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_FOO_POINTER; 
     return NULL; 
    } 

    // foo_ptr must refer to a foo variant of type blue_foo 
    if (foo_ptr->type != blue_foo) { 
     if (status != NULL) *status = FOO_STATUS_INVALID_FOO_VARIANT; 
     return NULL; 
    } 

    // ***** Actual Work Goes Here ***** 

    ... 

} // end foo_blue_init_with_bar 

在另一方面,如果你不遵守契約式設計,那麼它可以算得上是個人喜好的問題。

5

我個人一起去的第一部分。 儘早退回。

+0

我會想象一個體面的編譯器會優化第二種情況,以便它實際上早日返回。沒有? – amn 2010-03-06 15:43:05

+0

誰在這個答案中說了關於編譯器優化的任何事情? – Anurag 2010-03-06 17:44:36

0

我選擇第二個希望會更好,因爲它更好地描述了邏輯,常用的最佳實踐是你最好只在函數內部有一個return語句。

由於性能原因,我不會選擇第一個,因爲編譯器無論如何都會進行優化,我更喜歡可讀的代碼。

+1

爲什麼最好的做法是在一個函數中使用1個return語句? – 2010-03-06 06:09:06

+1

這是一個_old_「公理」,在方法中只有一個返回會降低複雜性。這些日子並不常見,但仍然泛泛而談。 – 2010-03-06 06:10:39

8

問題不是很清楚,在這裏。如果我們比較這:

if (c1) return false; 
if (c2) return false; 
if (c3) return false; 
return true; 

有了這個:

if (!c1) { 
    if (!c2) { 
    if (!c3) { 
     return true; 
    } 
    } 
} 
return false; 

然後我會投票給都不是。做到這一點,而不是:

return !c1 && !c2 && !c3; 

如果問題是關於多回報是否是可以接受的,那麼這個問題已經討論過了(見:Should a function have only one return statement

2

第一種方法看起來aestheticly更好,但我不不認爲它完全捕捉你想要做的事情。如果所有的條件在邏輯上屬於一起,那麼把它們放在一個if語句中(我把布爾操作符放在行的開頭,以便在測試過程中更容易註釋單個條件(從大的SQL WHERE子句掛起) ) :

if(a > 0 
     && strlen(str) > a 
     && str[a] != NULL) 
    { 
     return true; 
    } 

或者,返回一個布爾值時(這是一個特例,從而可能不適用於你的問題):

return (a > 0 
     && strlen(str) > a 
     && str[a] != NULL); 
1

如果條件具有類似或相關的語義,它可能是更好的使用邏輯表達式而不是一堆if s。喜歡的東西

return a > 0 && strlen(str) > a && str[a] != NULL; 

但是如果你有使用if,最好的方法來組織分支經常(如果不總是)取決於if本身的性質。

大部分時間代碼中的if都是「不平衡的」:它們或者只有一個分支(沒有else),或者其中一個分支是「重量級」,而其他分支是「輕量級」。在這種情況下,首先處理「簡單」分支總是更好,然後明確指出該分支的處理已結束。這是通過使用你的第一個變體問題來實現的:檢測需要「簡單」處理的情況,做到這一點,並立即return(如果你必須離開功能)或做continue(如果你必須進行下一步循環的迭代)。這是立即return(或continue),幫助讀者瞭解,這個分支已經完成,並且不需要再擔心它。所以,一個典型的函數看起來如下:一些if s「攔截」簡單情況,立即處理它們(如果需要)和return。只有在處理了所有簡單化的案例後,通用的「重」處理纔開始。像這樣組織起來的函數比帶有一堆嵌套的函數(比如你的第二個變體)更容易閱讀。

有些人可能會認爲從函數中間的return或在週期中間的continue違背了「結構化編程」的想法。但不知何故,人們錯過了「結構化編程」這個概念從未被用於實際應用的事實。不,早期的「return」原則(或「continue早期」)明顯提高了代碼的可讀性,因爲它明確強調了該分支的處理已結束的事實。在「結構化」if的情況下,這樣的事情就不那麼明顯了。

重新開始:避免嵌套if s。他們很難閱讀。避免「不平衡」if s。他們也很難閱讀。首選「扁平」分支風格,首先處理簡單案例,並立即通過明確的returncontinue完成處理。

0

第一種方法的主要原因是縮進保持在理智的水平。超過兩個級別的任何事情開始變得難以管理。就個人而言,我嘗試在大多數時間內不超過方法內的單個縮進級別。

但是,對於我而言,至少對於我來說,第二種選擇更清晰,因爲邏輯看起來很簡單。當滿足所有三個條件時,我們返回true。我傾向於將每個布爾結果存儲到一個變量中,該變量的名稱對於傳遞的值有意義,並返回它們的AND值。

isPositive = a > 0; 
isStringLonger = str.length() > a; 
isCharacterNonNull = str[a] != NULL; 

return isPositive && isStringLonger && isCharacterNonNull; 

我剛剛根據你的例子做了名字,所以他們看起來像垃圾,但重點是他們不應該。

或者,當這三個變量看起來很多時,我會在返回之前將它們合併成一個變量。

isValidString = isPositive && isStringLonger && isCharacterNonNull; 
return isValidString; 

就多重返回語句而言,我發現Ruby爲大多數語句添加後綴的方式非常乾淨和可讀。

return false if a <= 0 
return false if str.length() <= a 
return false if !str[a].nil? 
return true 
0

你的兩個樣本做不同的事情(見下面的代碼)。我寧願第二,如果它是這樣寫下面的(也許除了用更少的括號內。

if((a>0) && (strlen(str) > a) && (str[a] != NULL)) 
{ 
    return true; 
} 

或寫的

return ((a>0) && (strlen(str) > a) && (str[a] != NULL)) 

要回答你的問題。我更喜歡第一(除上面的代碼是正確的),我總是更喜歡

  1. 更少tabbing。我更喜歡一切儘可能留下你的第二個例子中斷(但不是我的)
  2. 如果可能,返回頂部如果(cond)返回等等; else {code(); }
  3. 在頂部和較長的代碼短碼在底部

    如果(COND) { 碼(); } else if(cond2) { code(); code(); } else { code(); code(); code(); }