2008-12-10 69 views
16

我和一位同事對以下哪一項更優雅有爭議。我不會說誰是誰,所以這是公正的。哪個更優雅?從一個比if語句更優雅的函數早期返回?

public function set hitZone(target:DisplayObject):void 
     { 
      if(_hitZone != target) 
      { 
       _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver); 
       _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut); 
       _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown); 

       _hitZone = target; 

       _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true); 
       _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true); 
       _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true); 
      } 
     } 

......或者......

public function set hitZone(target:DisplayObject):void 
     { 
      if(_hitZone == target)return; 

      _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver); 
      _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut); 
      _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown); 

      _hitZone = target; 

      _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true); 
      _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true); 
      _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true); 

     } 
+1

這是重複的。請檢查這個帖子:http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement – 2008-12-10 11:05:49

+1

這個問題是不是重複的,雖然是最好的答案似乎也回答我的問題 – Iain 2008-12-10 11:09:08

回答

16

這是可以違反規則(即最佳實踐)的情況之一。一般而言,您希望儘可能少的返回點。實際的原因是它簡化了你對代碼的閱讀,因爲你總是可以假設每一個函數都會接受它的參數,執行它的邏輯並返回結果。爲各種情況提供額外的回報往往會使邏輯複雜化,並增加讀取和完善代碼所需的時間。一旦你的代碼達到了維護階段,那麼當他們嘗試破譯邏輯時,多次返回會對新程序員的生產力產生巨大影響(當評論稀少且代碼不清時,它尤其糟糕)。問題相對於函數的長度呈指數增長。

那麼,爲什麼在這種情況下,每個人都喜歡選擇2?這是因爲您正在設置一個合同,該合同通過驗證傳入數據或其他可能需要檢查的不變量來強制執行該功能。構建驗證的最漂亮的語法是檢查每個條件,如果條件無效,則立即返回。這樣,您不必通過所有檢查來維護某種類型的isValid布爾值。

總結的事情了:我們真的正在研究如何編寫驗證代碼,而不是一般的邏輯;選項2更適合驗證碼。

37

在大多數情況下,早期回國降低了複雜性,使代碼更易讀。

這也是在Spartan programming應用的技術之一:

使用最少的控制

  1. 通過使用專門的 結構,例如三元化, 繼承和類儘量減少使用條件的如 默認,類一次和類 分離器
  2. 早期return簡化條件。
  3. 通過使用動作施加器最小化循環構造,例如Class Separate和 Class FileSystemVisitor。
  4. 使用提前退出簡化迭代邏輯(通過return, continuebreak語句)。

在你的例子中,我會選擇選項2,因爲它使代碼更具可讀性。檢查功能參數時使用相同的技術。

+2

查看了Spartan編程鏈接後,我不得不說我不完全喜歡我所看到的。幾乎沒有空白?簡短的變量名稱?對於沒有大括號的循環?嗯... – Iain 2008-12-10 14:04:22

+0

我同意。我沒有讀過它,但是:沒有空白,簡短的varnames,沒有curlies的循環 - 所有我不喜歡的東西。 iwwwww – 2008-12-11 01:09:02

0

在這種情況下(一個測試,沒有其他條款)我喜歡測試和返回。它清楚地表明,在那種情況下,沒有什麼可做的,而不必閱讀函數的其餘部分。

但是,這是分裂最好的頭髮。我相信你一定有更大的問題擔心:)

1

啊監護人。

Imho,是的 - 它的邏輯更清晰,因爲返回是明確的並且緊挨着條件,並且它可以很好地與相似結構分組。這更適用於將「返回」替換爲「拋出新的異常」的情況。

0

選項2更具可讀性,但是當可能需要添加else時,代碼的可管理性會失敗。

所以如果你是肯定的,沒有別的去選擇2,但如果有可能爲其他條件是範圍的話,我寧願選擇1

11

只要早收益被組織成塊在函數/方法體的頂部,那麼我認爲它們比添加另一層嵌套更具可讀性。

我儘量避免在身體中間的早期回報。有時他們是最好的方式,但大多數時候我認爲他們會變得複雜。

此外,作爲一般規則,我儘量減少嵌套控制結構。很明顯,你可以把這個過分拿走,所以你必須使用一些自由裁量權。即使謂詞重複了一些子表達式(並且假設這不是一種語言中性能不重要的循環,以至於無法執行子表達式消除),將嵌套的if轉換爲單個開關/ case也更加清晰。特別是我不喜歡長函數/方法體中嵌套if的組合,因爲如果由於某種原因跳到代碼的中間,最終會上下滾動以精神上重構給定行的上下文。

0

選項1更好,因爲過程中您應該有最少數量的返回點。 有喜歡

 
    if (a) { 
    return x; 
    } 
    return y; 
的,因爲語言的工作方式

例外,但一般最好是有儘可能少出口點,因爲它是可行的。

1

如前所述,早期返回更具可讀性,特別是如果函數的主體很長,您可能會發現在3頁函數中錯誤地刪除了一個}(本身並不是很優雅),並試圖編譯它可能需要幾分鐘的非自動調試。

這也使得代碼更聲明,因爲這是你將它描述了另一個人的方式,因此可能是一個開發者,就足以一個接近去了解它。

如果功能後增加的複雜性,你有很好的測試,你可以簡單地包裹在一個新的功能,每個備選方案,並呼籲他們在分支機構的情況下,你十個分量聲明樣式的方式。

4

根據我的經驗,在項目中使用早期返回的問題是,如果項目中的其他人不習慣他們,他們將不會去尋找他們。如此早期的回報 - 如果涉及多個程序員,請確保每個人至少知道他們的存在。

我親自寫代碼,只要它可以返回,作爲延遲迴報往往引入了額外的複雜性如爭取安全退出一堆嵌套循環和條件。

所以,當我看到一個陌生的功能,我做的第一件事就是尋找一切return秒。真正有幫助的是設置語法着色,以使return與其他任何顏色不同。 (我選擇紅色)。這樣,return成爲確定功能的有用工具,而不是隱藏在粗心大意中的絆腳石。

0

我更喜歡以避免在一個函數的開始立即返回,並儘可能把符合條件的邏輯,以防止進入之前,它被調用的方法。當然,這取決於方法的目的。

但是,我並不介意在方法中間返回,只要方法簡短易讀。如果方法很大,在我看來,它已經不是很可讀了,所以它會被重構成帶有內聯返回的多個函數,或者我將在最後單獨返回一個控制結構。

0

我忍不住要關閉它的精確副本,因爲我看到一些類似的主題已經包括Invert 「if」 statement to reduce nesting具有很好的答案。

我會讓現在活...^_^

要作出這樣的回答,我是一個信徒,早期的回報後衛條款比深度嵌套IFS更好。

0

我已經看到了這兩種類型的代碼,我更喜歡,因爲它是看起來容易閱讀和理解對我來說第一個,但我看了很多地方,早期的存在是更好的路要走。

0

至少有一個其他的選擇。將實際工作的細節與關於是否執行工作的決定分開。像下面這樣:

public function setHitZone(target:DisplayObject):void 
    { 
     if(_hitZone != target) 
      setHitZoneUnconditionally(target); 

    } 

public function setHitZoneUnconditionally(target:DisplayObject):void 
    { 
     _hitZone.removeEventListener(MouseEvent.ROLL_OVER, onBtOver); 
     _hitZone.removeEventListener(MouseEvent.ROLL_OUT, onBtOut); 
     _hitZone.removeEventListener(MouseEvent.MOUSE_DOWN, onBtDown); 

     _hitZone = target; 

     _hitZone.addEventListener(MouseEvent.ROLL_OVER, onBtOver, false, 0, true); 
     _hitZone.addEventListener(MouseEvent.ROLL_OUT, onBtOut, false, 0, true); 
     _hitZone.addEventListener(MouseEvent.MOUSE_DOWN, onBtDown, false, 0, true); 

    } 

這三種(你的兩個加上面的第三個)是合理的小本的情況。然而,如果有一個長達數百行的函數,並且遍佈多個「紓困點」,那將是一件壞事。

0

我已經與我自己的代碼,多年來的辯論。我開始傾向於一次迴歸,並慢慢失敗。

在這種情況下,我寧願選擇2(一個返回),只是因爲我們講的,如果(),沒有其他複雜的代碼約7線由包裹着。它更具可讀性和功能性。它從上到下流動。你知道你從頂部開始,在底部結束。正如其他人所說的那樣,如果在開始時有更多的警衛或更復雜的功能或功能增長,那麼我寧願選擇1:在開始時立即返回以進行簡單驗證。