讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議111:採用異常鏈傳遞異常 >

建議111:採用異常鏈傳遞異常

設計模式中有一個模式叫做責任鏈模式(Chain of Responsibility),它的目的是將多個對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有對象處理它為止,異常的傳遞處理也應該採用責任鏈模式。

上一個建議中我們提出了異常需要封轉,但僅僅封轉還是不夠的,還需要傳遞異常。我們知道,一個系統友好性的標誌是用戶對該系統的「粘性」,粘性越高,系統越友好,粘性越低系統友好性越差,那問題是怎麼提高系統的「粘性」呢?友好的界面和功能是一個方面,另外一方面就是系統出現非預期情況時的處理方式了。

比如,我們的JEE項目一般都有三層結構:持久層、邏輯層、展現層,持久層負責與數據庫交互,邏輯層負責業務邏輯的實現,展現層負責UI數據的處理。有這樣一個模塊:用戶第一次訪問的時候,需要持久層從user.xml中讀取信息,如果該文件不存在則提示用戶創建之,那問題來了:如果我們直接把持久層的異常FileNotFoundException拋棄掉,邏輯層根本無從得知發生了何事,也就不能為展現層提供一個友好的處理結果了,最終倒霉的就是展現層:沒有辦法提供異常信息,只能告訴用戶說「出錯了,我也不知道出什麼錯了」——毫無友好性可言。

正確的做法是先封裝,然後傳遞,過程如下:

(1)把FileNotFoundException封裝為MyException。

(2)拋出到邏輯層,邏輯層根據異常代碼(或者自定義的異常類型)確定後續處理邏輯,然後拋出到展現層。

(3)展現層自行決定要展現什麼,如果是管理員則可以展現低層級的異常,如果是普通用戶則展示封裝後的異常。

明白了異常為什麼要傳遞,那接著的問題就是如何傳遞了。很簡單,使用異常鏈進行異常的傳遞,我們以IOException為例來看看是如何傳遞的,代碼如下:


public class IOException extends Exception{

//定義異常原因

public IOException(String message){

super(message);

}

//定義異常原因,並攜帶原始異常

public IOException(String message, Throwable cause){

super(message, cause);

}

//保留原始異常信息

public IOException(Throwable cause){

super(cause);

}

}


在IOException的構造函數中,上一個層級的異常可以通過異常鏈進行傳遞,鏈中傳遞異常的代碼如下所示:


try{

//Do Something

}catch(Exception e){

throw new IOException(e);

}


捕捉到Exception異常,然後把它轉化為IOException異常並拋出(此種方式也叫作異常轉譯),調用者獲得該異常後再調用getCause方法即可獲得Exception的異常信息,如此即可方便地查找到產生異常的根本信息,便於解決問題。

結合上一個建議來看,異常需要封裝和傳遞,我們在進行系統開發時不要「吞噬」異常,也不要「赤裸裸」地拋出異常,封裝後再拋出,或者通過異常鏈傳遞,可以達到系統更健壯、友好的目的。