[轉載自JuvenXu]Java異常處理最佳實踐

原文鏈接:[url]http://www.juvenxu.com/2011/03/30/exception-handling-best-practices/[/url]

作爲一個已經寫了近5年Java代碼的程序員,我直到最近纔算是基本明白了異常應該怎麼用,這真是令人汗顏。事情是這樣的,上週,和往常一樣,我在開發一個很平常的應用,並且不得不面對各種各樣的異常,比如常見的IOException,或者用到個第三方類庫可能會給你返回ThirdPartyException,還有,我自己也會定義異常,姑且叫它MyOwnException。我是使用分層的架構寫代碼的,比如有個REST層,有個領域模型層,有持久化層,這本沒什麼問題,可當我發現一個接口要throws三四個或者更多的異常的時候,就覺得蛋疼了,這不僅看起來噁心,如調用者如何處理它們似乎也是個問題。這不是第一次,其實之前蛋疼過很多次了,我一直閱讀各種OO設計相關書籍,以理解如何組織各種類和對象,可我還真沒仔細考慮過如何組織異常。

好吧,爲了以後不再蛋疼,我得弄清楚這個異常的用法。我希望能夠明白如何組織異常才能使其變得整潔,而不是肆無忌憚地污染我精心設計的接口。

我翻閱了相關書籍,仔細查看了它們關於異常的描述,包括
[list]
[*]Implementation Patterns — Kent Beck
[*]Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin
[*]Effective Java (2ed) — Joshua Bloch
[*]Robust Java: Exception Handling, Testing, and Debugging — Stephen Stelting
[/list]
邊閱讀邊思考,最終基本理解了異常處理的最佳實踐。值得一提的是,這幾本書中 Joshua Bloch 的描述相比最爲全面和深刻,Kent Beck 的最簡單粗糙,Robert C. Martin 一書的那一章其實是 Michael Feathers 寫的,也只能算是一般,Stephen Stelting 的描述看起來很全面,可太理論了,實例太少,沒有說服力。

那異常處理的最佳實踐到底是什麼?首先要理解的是,異常到底是什麼,我覺得異常分兩類。

第一是業務異常,就是處理業務的時候80%的時候是沒問題的,但可能有20%的時候事情沒有按理想的方向發展。例如註冊用戶的時候,正常情況是註冊成功,但可能用戶提交請求的時候,系統發現用戶名已經被別人註冊了,這是就可以拋出一個UserAlreadyExistsException。

第二是系統異常,系統異常與具體業務流程沒有直接的關係,例如編程錯誤導致的NullPointExcpetion,還有壞境問題,例如磁盤損壞或者網絡連接不穩定造成了IOException。

先談談業務異常,業務異常應該是接口的一部分,該接口的用戶應該能夠處理該異常。也就是說,在實現接口之前,就應該定義清楚這個接口會拋出怎樣的異常,例如註冊用戶這個接口就應該明確定義會拋出UserAlreadyExistsException,並輔以必要文檔,那調用者就知道怎麼去處理。如果你在接口聲明異常,那你一定要確保接口的調用者能夠處理之。例如對應於 UserAlreadyExistsException,用戶界面可以提示用戶該ID已存在,那用戶可以使用另外的ID來註冊。注意,處理異常不是簡單的catch,然後隨便打印點日誌那麼簡單,業務異常的處理應當遵照業務流程。

而系統異常由於沒有實際業務意義,調用者往往是不知道如何處理的。例如註冊用戶的時候收到一個IOException,你只會一頭霧水。那系統異常應該如何處理呢?有這麼幾個選擇:

能在底層處理的就本地處理掉,例如網絡連接超時異常,可以編寫代碼嘗試再次連接。
能翻譯爲業務異常的就翻譯,有時候你可能發現某個IOException對應有實際的業務意義,但不要直接拋出,而是應該Exception Chaining技術來翻譯,例如 throw new MyOwnException(“sorry, …”, ioe),這樣既不至於丟失原始信息,業務接口也更容易理解。
實在無法處理的,就轉換爲RuntimeException,反正調用者也不知道怎麼處理,那在接口中聲明是沒有意義的,只會造成調用者困惑並帶來負擔。這裏同樣應該使用Exception Chaining以避免信息丟失。我的實際經驗告訴我,很多人完全都不考慮使用RuntimeException,這真是莫大的浪費。
有時候業務異常和系統異常的角色會相互轉換,例如 FileNotFoundException 對於文件處理這個領域來說是業務異常,可對於我的應用來說,可能只是一個系統異常。

上述是我認爲異常處理最核心最有用的實踐,除此之外還有不少前人總結的經驗,包括:

記錄異常,但只記錄一次。(不要丟失歷史)
不要返回及傳Null值。(避免NullPointException和不必要的檢查)
儘量重用JDK自帶的異常。(以降低學習成本)
在自定義異常中存儲異常相關信息字段。(以方便日後處理)
永遠不要直接忽略異常。(不要丟失歷史)
爲異常聲明寫Javadoc。(對你的用戶有好些)
不要 throws 或者 catch 頂層的 Exception 類。(天知道Exception對應什麼業務信息)
其實當我讀畢前面提到的幾本書,然後再Google相關文檔的時候,就發現已經有人基本總結過這些內容了:Best Practices for Exception Handling, by Gunjan Doshi, 文章是2003年寫的,但一點也不過時。相比之下我這篇文章有污染搜索引擎之嫌,不管怎樣,這算是自我的一個總結,對英文不好的人應該也有些價值。

原文鏈接:[url]http://www.juvenxu.com/2011/03/30/exception-handling-best-practices/[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章