讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議112:受檢異常盡可能轉化為非受檢異常 >

建議112:受檢異常盡可能轉化為非受檢異常

為什麼說是「盡可能」的轉化呢?因為「把所有的受檢異常(Checked Exception)都轉化為非受檢異常(Unchecked Exception)」這一想法是不現實的:受檢異常是正常邏輯的一種補償處理手段,特別是對可靠性要求比較高的系統來說,在某些條件下必須拋出受檢異常以便由程序進行補償處理,也就是說受檢異常有合理的存在理由,那為什麼要把受檢異常轉化為非受檢異常呢?難道說受檢異常有什麼缺陷或不足嗎?是的,受檢異常確實有不足的地方:

(1)受檢異常使接口聲明脆弱

OOP(Object Oriented Programming,面向對像程序設計)要求我們盡量多地面向接口編程,可以提高代碼的擴展性、穩定性等,但是一旦涉及異常問題就不一樣了,例如系統初期是這樣設計一個接口的:


interface User{

//修改用戶名密碼,拋出安全異常

public void changePassword()throws MySecurityException;

}


隨著系統的開發,User接口有了多個實現者,比如普通的用戶UserImpl、模擬用戶MockUserImpl(用作測試或系統管理)、非實體用戶NonUserImpl(如自動執行機、邏輯處理器等),此時如果發現changePassword方法可能還需要拋出RejectChangeException(拒絕修改異常,如自動執行機正在處理任務時不能修改其密碼),那就需要修改User接口了:changePassword方法增加拋出RejectChangeException異常,這會導致所有的User調用者都要追加對RejectChangeException異常問題的處理。

這裡產生了兩個問題:一是異常是主邏輯的補充邏輯,修改一個補充邏輯,就會導致主邏輯也被修改,也就是出現了實現類「逆影響」接口的情景,我們知道實現類是不穩定的,而接口是穩定的,一旦定義了異常,則增加了接口的不穩定性,這是對面向對像設計的嚴重褻瀆;二是實現的類變更最終會影響到調用者,破壞了封裝性,這也是迪米特法則所不能容忍的。

(2)受檢異常使代碼的可讀性降低

一個方法增加了受檢異常,則必須有一個調用者對異常進行處理,比如無受檢異常方法doStuff是這樣調用的:


public static void main(Stringargs){

doStuff();

}


doStuff方法一旦增加受檢異常就不一樣了,代碼如下:


public static void main(Stringargs){

try{

doStuff();

}catch(Exception e){

e.printStackTrace();

}

}


doStuff方法增加了throws Exception,調用者就必須至少增加4條語句來處理該異常,代碼膨脹許多,可讀寫性也降低了,特別是在多個異常需要捕捉的情況下,多個catch塊多個異常處理,而且還可能在catch塊中再次拋出異常,這大大降低了代碼的可讀性。

(3)受檢異常增加了開發工作量

我們知道,異常需要封裝和傳遞,只有封裝才能讓異常更容易理解,上層模塊才能更好的處理,可這也會導致低層級的異常沒玩沒了的封裝,無端加重了開發的工作量。比如FileNotFoundException在持久層拋出,就需要定義一個MyException進行封裝,並拋出到上一個層級,於是增加了開發工作量。

受檢異常有這麼多的缺點,那有沒有什麼辦法可以避免或減少這些缺點呢?有,很簡單的一個規則:將受檢異常轉化為非受檢異常即可,但是我們也不能把所有的受檢異常轉化為非受檢異常,原因是在編碼期上層模塊不知道下層模塊會拋出何種非受檢異常,只有通過規則或文檔來約束,可以這樣說:

受檢異常提出的是「法律下的自由」,必須遵守異常的約定才能自由編寫代碼。

非受檢異常則是「協約性質的自由」,你必須告訴我你要拋出什麼異常,否則不會處理。

以User接口為例,我們在聲明接口時不再聲明異常,而是在具體實現時根據不同的情況產生不同的非受檢異常,這樣持久層和邏輯層拋出的異常將會由展現層自行決定如何展示,不再受異常的規則約束了,大大簡化開發工作,提高了代碼的可讀性。

那問題又來了:在開發和設計時什麼樣的受檢異常有必要轉化為非受檢異常呢?「盡可能」是以什麼作為判斷依據呢?受檢異常轉換為非受檢異常是需要根據項目的場景來決定的,例如同樣是刷卡,員工拿著自己的工卡到考勤機上打考勤,此時如果附近有磁性物質干擾,則考勤機可以把這種受檢異常轉化為非受檢異常,黃燈閃爍後不做任何記錄登記,因為考勤失敗這種情景不是「致命」的業務邏輯,出錯了,重新刷一下即可。但是到銀行網點取錢就不一樣了,拿著銀行卡到銀行取錢,同樣有磁性物質干擾,刷不出來,那這種異常就必須登記處理,否則會成為威脅銀行卡安全的事件。匯總成一句話:當受檢異常威脅到了系統的安全性、穩定性、可靠性、正確性時,則必須處理,不能轉化為非受檢異常,其他情況則可以轉換為非受檢異常。

注意 受檢異常威脅到系統的安全性、穩定性、可靠性、正確性時,不能轉換為非受檢異常。