簡體   English   中英

您如何編寫方法的錯誤檢查部分以使其可讀並易於出錯?

[英]How do you write the error checking part of a method to make it readable and error prone?

我與另一位程序員在如何編寫帶有大量錯誤檢查的方法上存在分歧:

public void performAction() {
  if (test1) {
    if (test2) {
      if (test3) {
        // DO STUFF
      } else {
        return "error 3";
      }
    } else {
      return "error 2";
    }
  } else {
    return "error 1";
  }
}

-

public void performAction() {     
  if (!test1) {
    return "error 1";
  }
  if (!test2) {
    return "error 1";
  }
  if (!test3) {
    return "error 1";
  }
  // DO STUFF
}

對我而言, if語句的深層嵌套使第一個示例難以閱讀。
盡管有三個return s,第二個更具可讀性。

我好奇地檢查了Code Complete在說些什么,這使我不太確定如何處理此問題:

嵌套底部的錯誤條件堆棧是編寫正確的錯誤處理代碼的標志。

但是之后

在四個if語句中縮進例程的主體在外觀上很難看,特別是如果最里面的if語句中有很多代碼。

並考慮使用保護子句,如第二個示例

最小化每個例程中的返回數。 當在底部閱讀該例程時,您不知道該例程是否有返回上面某個位置的可能性時,很難理解該例程。

您如何編寫方法的錯誤檢查部分以使其易讀且容易出錯?

沒有什么比風格上的辯論更快地使程序員參與戰斗了( 在支持循環和功能的語言中使用“ goto”是否有優勢?如果是,為什么呢? )。 因此,簡短的答案是“您和您的團隊決定采用哪種樣式最適合您的項目/語言。”

話雖如此,我想在Code Complete關於多次收益的評論中加上2美分。 您應該區分多個成功的回報和多個回報。 如果我需要調查不是由於錯誤生成的5個返回,則可能需要重寫該函數。 如果您在檢測到錯誤后立即退出函數,那么維護程序員(即6個月內)在遵循函數的主要邏輯方面應該比嵌套所有這些錯誤檢查沒有更多麻煩。

因此,我個人認為您的第二個代碼示例是更好的選擇。

這是我的意見。

“減少每個例程的返回次數”這一古老口號似乎有些過時了。 當您的方法具有8-10行以上的代碼,並且其中執行大量操作時,此方法非常適用。

強調單一責任和非常簡短的方法的較新的思想流派似乎使這有點不必要。 當您的整個方法不直接進行任何操作,而僅處理錯誤處理時,最好使用純凈格式的多次返回。

無論哪種情況,無論何時嵌套ifs,可讀性都會受到很大影響。

我要進行的一種優化是使用if-else-if結構,以清楚地指示邏輯流程。

樣例代碼:

public void Execute() 
{     
    if (test1)
    {
        return;
    }
    else if (test2)
    {
        return;
    }
    PerformAction();
}

private void PerformAction()
{
    //DO STUFF
}

如果您使用的語言具有異常處理和自動資源管理功能,那么在遇到外部輸入錯誤的情況下,您的同事可能應該習慣於使用首選樣式並過早退出。

在異常處理和自動資源管理(例如:沒有析構函數的語言或GC之類的GC)之前,嘗試將函數出口移到作用域底部的想法非常有用,因為錯誤恢復通常需要手動清除

在那些手動清理的情況下,將出口移至函數底部通常很有用,這樣您就可以在函數的頂部查看邏輯,以創建函數所需的臨時資源,並向函數底部移動。看到這些資源的對稱清理。

在諸如匯編的情況下,很常見的是在函數底部會出現清除的jumps/branches到錯誤標簽。 即使在為此目的而使用gotos C語言中,這種情況也並不罕見。

同樣,如前所述,深度嵌套會帶來很多精神上的負擔。 您的大腦必須像深度嵌套的堆棧一樣工作,以記住您所在的位置,甚至頑固的C程序員Linus Torvalds都喜歡說:如果您在單個函數中需要4個嵌套的縮進級別,您的代碼已經損壞,應該進行重構(我不確定我是否同意他的確切數字,但是他在混淆邏輯方面有其道理)。

當您使用C ++等更現代的語言時,現在可以通過析構函數進行自動資源管理。 然后,函數不應再提及清除細節,因為資源應通過遵循所謂的資源獲取即初始化習慣用法(並非最佳名稱)來自動處理清除。 這消除了偏向於努力使錯誤處理邏輯趨於底層的樣式的重要原因之一。

最重要的是,當您使用C ++之類的語言時,它可能會引發異常,並且到處都是。 因此,每隔一行代碼具有如下所示的隱藏,隱式退出的效果並不少見:

if an exception occurs:
    automatically cleanup resources and propagate the error

因此,到處都有隱藏的,過早的出口。 因此,如果您使用這樣的語言,則不僅會在出現異常情況時習慣於過早退出,而且還被迫陷入其中而別無選擇。 就這些語言的可讀性/可追溯性而言,沒有比這更簡單的了:

if something bad happened:
     return error

我建議該規則的一個例外是靜態分支預測。 如果您要編寫對性能至關重要的代碼,那么微效率中的最小數量比可讀性更重要,那么您就希望您的分支機構能夠像英特爾建議的那樣,偏向於支持普通情況下的執行力。 所以代替:

if something exceptional happened:
    return error

...為了提高性能,您可以反轉邏輯並改為執行以下操作:

if something normal happened:
     ...
     return success
return error

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM