無瑕的程式碼 (7+8):錯誤處理+邊界

Larry Chien
嗨,世界
Published in
6 min readSep 1, 2020
Photo by Matt Noble on Unsplash

前言

看完《無暇的程式碼 (6):物件和資料結構》之後,我們來看看要怎麼將程式碼中的錯誤處理(Error Handling)寫得簡單乾淨,而在撰寫程式(尤其是測試)的時候,又該怎麼處理、定義我們撰寫的程式的邊界(Boundaries)呢。

要怎麼把錯誤處理寫得乾淨簡潔

  1. 使用例外事件而非回傳錯誤碼
  2. 在開頭寫下你的 Try-Catch-Finally 敘述
  3. 使用不檢查型例外(Use Unchecked Exception)
  4. 提供發生例外的相關資訊
  5. 從呼叫者的角度定義例外類別
  6. 定義正常的程式流程
  7. 不要回傳或是傳遞空值(null)

使用例外事件而非回傳錯誤碼

針對回傳錯誤碼做處理是好事,但不應該讓「處理錯誤碼」的程式碼模糊掉原本的程式邏輯。因此使用例外事件來處理錯誤會比較合適。

在開頭寫下你的 Try-Catch-Finally 敘述

在 try 的區塊執行程式、執行中斷了會接續在 catch 區塊繼續執行。如果讀者在電商領域,應該對交易處理(transaction)不太陌生。某種程度上 try 就像是交易處理,catch 則是讓程式碼保持一致狀態。

也因為要讓程式碼在意外中斷時還能保持一致的狀態,需要養成讓 try-catch-finally 成為程式開頭敘述的習慣

使用不檢查型例外(Use Unchecked Exception)

書中的這個章節主要是針對 Java 的不檢查行例外做描述。對於使用 Java 的程式語言的讀者,建議可以參考 Teddy 的《例外處理設計與重構實作》和《搞懂Java例外處理的難題:Checked與Unchecked Exceptions不再是問題》(不懂 Java 也能看得懂啦!)

[備註]動態語言 JavaScript / Ruby 沒有 checked exception 所以這點可以忽略。

提供發生例外的相關資訊

例外發生的時候,不外乎是需要讓使用者知道發生什麼事,再來就是要讓開發人員知道足夠多的資訊進行排查。因此需要產生有益的錯誤訊息,包含:哪個操作導致錯誤發生、錯誤型態……等等資訊。

從呼叫者的角度定義例外類別

舉個例子:在撰寫呼叫 API 的函式時,針對 HTTP Request 的例外狀況,讀者會逐一在每個呼叫的函式撰寫例外的判斷,還是會將呼叫的函式做成共用的函式,並在共用函式中處理各種 HTTP Request 的例外狀況呢?

書中的這個小節建議我們可以將呼叫 API 的函式打包,讓共用函式去處理這些例外狀況,同時也呼應到前述說的「不應該讓『處理錯誤碼』的程式碼模糊掉原本的程式邏輯」。

定義正常的程式流程

書中想描述的是「特殊情況模式」的程式碼。我們團隊認為:常常發生的就不應該是「例外」,而是應該在程式碼裡面處理。

不要回傳或是傳遞空值(null)

一言以蔽之就是:你不會想要在每個區塊都在做判斷 null 檢查的。如果有個地方忘記檢查,那只會造成大災難。

Photo by Brett Jordan on Unsplash

要怎麼把邊界定義清楚

由於這章節各小節的篇幅不長,僅挑選出幾個印象較深刻、有些心得的小節跟讀者分享。

  1. 探索及學習邊界
  2. 學習式測試比不花工夫更好
  3. 簡潔的程式邊界

探索、學習邊界,使用「學習式測試」

第三方軟體幫助我們可以更快地進行開發,而我們在使用時,為了確保程式的穩定性,建議還是要針對第三方軟體撰寫一些測試。確保程式有在使用的語法是否仍有支援、使用的方式和回傳的形式是不是有改動……等等。

而學習式測試是在幫助我們了解第三方軟體的成長和改變。我們可以藉由對第三方程式撰寫測試,在每次第三方軟體更新的時候,執行這些學習式測試,藉此學習了解第三方軟體有無行為上的改變。透過這些「邊界測試」確保上述提到的語法、使用方式的穩定。

以 React 來說,componentWillUpdate 這個語法現在已經建議改成 UNSAFE_componentWillUpdate 了,原因是因為該方法將在 React 17 版之後就會 deprecated。但如果我們不知道其在 17 版會被 deprecated 的情況下,升級成 React 17 版,則原本有使用該方法的地方就會產生問題。因此作者建議我們在開發時,可以針對第三方軟體撰寫測試,當軟體更新之後,只要相對應的測試沒通過,我們就能第一時間發現。

簡潔的程式邊界

有趣的事通常會發生在邊界上,改變是其中之一。而好的軟體能適應這些改變,不需要大規模投入心力或重新撰寫程式。當我們使用無法控制的程式碼時,就需要特別花功夫去保護已經投注的心力,並確保不用花太多功夫就能因應未來的修改。

減少「直接引用第三方軟體」的程式碼是其中一種可以考慮的方式。例如:程式碼都是透過引用我們撰寫的 utils 來呼叫第三方軟體,而當第三方軟體發生改變時,我們只需要調整 utils 裡面的檔案,就可以繼續開發。另外一種方式則是使用 adapter pattern,在我們開發的軟體跟第三方軟體之間建立 interface,透過將我們開發的軟體封裝成第三方軟體看得懂的格式來跟第三方軟體溝通(或者是反過來把第三方軟體的格式轉成我們看得懂的格式)。

Photo by Milan Seitler on Unsplash

小結

如果你想要參考 Clean code 範例,可以參考 JavaScript 的範例另一個 JavaScript 參考Ruby 的範例。當使用者足夠多的時候,任何狀況都有可能會發生,因此對於例外狀況的處理就會顯得格外重要。作者在第七章的總結寫得很貼切:

雖然 Clean Code 是易讀的,但它也必須是耐用的。

唯有將錯誤處理、程式邊界認真看待,才能讓我們的程式碼更加的簡潔無暇。

--

--