我從 1000 份程式碼審查中學到了什麼

飛哥的咖啡發表於2018-03-12

我看到有些建議不斷重複出現,所以我決定整理一個清單,與諸位分享。

這是我的 3(額外 +1)個最常見的程式碼審查建議。

我從 1000 份程式碼審查中學到了什麼

建議1:出錯時丟擲異常

我見過的一個常見模式是:

這種模式實際上導致了我做的移動應用程式崩潰,我們當時使用的搜尋後端開始丟擲異常。而應用程式的 API 伺服器中也有一些類似的程式碼。從應用程式的角度來看,它成功獲取了 200 個響應,只是每個搜尋請求返回的是空列表。

如果當時 API 直接丟擲異常,那麼我們的監控系統就會立即檢測到並修復它。

很多時候,當你捕獲一個異常時,你會希望它返回一個空物件。Java 中的空物件包括 Optional.empty()、null 和空列表,而它們在 URL 解析中經常出現。如果 URL 不能從一個字串中正確解析時,先不要返回 null,而是停下來問問自己:“為什麼 URL 格式不正確?這不是我們應該在上游解決的資料問題嗎?”

空物件不是解決此類問題的合適工具。如果有異常,你應該(及時)丟擲它。

建議2:使用盡可能具體的型別

這個建議基本上與字串型別程式設計相反。

我經常看到類似這樣的程式碼:

儘可能具體的型別可以讓你避免整個類的錯誤,這基本上是大家選擇強型別語言(如 Java)的原因。

所以現在的問題是:那些想要寫強型別語言的程式設計師,最終是怎麼寫出糟糕的字串型別的程式碼哪?答案是:因為外部世界不是強型別的。字串通常來自許多不同的地方,比如:

  • url 中的請求和路徑引數
  • JSON
  • 不支援列舉的資料庫
  • 寫得很差的庫

這些情況下,你應該使用以下策略來避免這個問題:將字串解析和序列化保持到程式邊緣。這是一個例子:

它帶給我們許多好處:1、能夠立刻發現格式不正確的資料;如果有任何問題,應用程式會提前顯示並終止。2、一旦資料通過驗證,你不必在整個應用程式中捕獲解析異常。3、強型別使得簽名具有更多資訊,這樣你就無需在每個方法上寫 javadoc。

建議3:使用 Optionals 而不是 null

Optional(可選)類是 Java 8 中最好的特性之一,它表示一個可以合理存在或不存在的實體。

問題時間:唯一用縮寫來指代的異常是什麼?答:NPE (Null Pointer Exception,空指標異常)。這是迄今為止 Java 中最常見的異常,被稱為十億美元的錯誤

Optional 允許你從程式中完全刪除 NPE。但是,你必須正確地使用它。這裡有一些關於如何使用 Optional 的建議:

  • 你不應該在任何時候隨意呼叫 .get(),而應仔細考慮 Optional 不存在的情況,並給出合理的預設值。
  • 如果你還沒有一個合理的預設值,那麼像 .map().flatMap() 這樣的方法,可以讓你晚點做這個決定。
  • 如果外部庫返回 null 來表示空值,則立即使用 Optional.ofNullable() 以封裝它。相信我,以後你會感謝自己的。在程式內部,null 有往上“冒泡”的傾向,所以還是最好從源頭上阻止它們。
  • 在返回型別的方法中使用 Optional。這樣做可以讓你不用閱讀 javadoc 就可以獲知,值是否可能不存在。

額外建議:儘可能使用“Unlift”方法

你應該儘量避免類似的方法:

所有要避免的方法有什麼共同點?他們都使用容器物件,比如 Optional,List 或 Task 作為方法的引數。當返回型別是同一型別的容器(即,一個引數的方法採用 Optional 並返回一個 Optional),情況會變得更糟糕。

為什麼?

相比 ① Promise<A> method(Promise<B> param),② A method(B param) 更具有靈活性。

如果你有 Promise<B>,那麼你可以使用 ①,也可以通過  .map 函式“提升(lifting)” (比如 promise.map(method))來使用 ②.

但是,如果你只有 B,那麼你可以很容易地使用 ②,卻不能使用 ①,很明顯,② 是一個更靈活的選擇。

我喜歡稱之為“Unlift”,因為它與普通函式的實用方法“Lift”恰好相反。應用這些重寫能使方法更靈活,呼叫更輕鬆。

相關文章