(點選上方公眾號,可快速關註)
來源:ImportNew – 唐尤華 ,
合理地使用異常處理可以幫你節省數小時(甚至數天)除錯時間。一個乘法異常會毀掉你的晚餐乃至週末計劃。如果處置不及時,甚至對你的名譽都會造成影響。一個清晰的異常處理策略可以助你節省診斷、重現和問題糾正時間。下麵是6條異常處理建議。
1. 使用一個系統全域性異常類
不必為每種異常型別建立單獨的類,一個就夠了。確保這個異常類繼承RuntimeException,這樣可以減少類個數並且移除不必要的異常宣告。
我知道你正在想什麼:如果型別只有一個,那麼怎麼能知道異常具體是什麼?我將如何追蹤具體的屬性?請繼續閱讀。
2. 使用列舉錯誤碼
我們大多被教授的方法是將異常轉為錯誤資訊。這次檢視日誌檔案時很好,(呃)但是這樣也有缺點:
-
錯誤資訊不會被翻譯(除非你是Google)
-
錯誤資訊不會轉換為使用者友好的文字
-
錯誤資訊不能用程式設計的方式檢測
將異常訊息留給開發者定義也會出現同樣的錯誤有多種不同的描述。
一個更好的辦法是使用列舉表示異常型別。為每個錯誤分類建立一個列舉(付款、認證等),讓列舉實現ErrorCode介面並作為異常的一個屬性。
當丟擲異常時,只要傳入合適的列舉就可以了。
throw new SystemException(PaymentCode.CREDIT_CARD_EXPIRED);
現在如果需要測試異常只要比較異常程式碼和列舉就可以了。
catch (SystemException e) {
if (e.getErrorCode() == PaymentCode.CREDIT_CARD_EXPIRED) {
…
}
}
透過將錯誤碼作為查詢資源的key就可以方便地提供友好的國際化文字。
public class SystemExceptionExample3 {
public static void main(String[] args) {
System.out.println(getUserText(ValidationCode.VALUE_TOO_SHORT));
}
public static String getUserText(ErrorCode errorCode) {
if (errorCode == null) {
return null;
}
String key = errorCode.getClass().getSimpleName() + “__” + errorCode;
ResourceBundle bundle = ResourceBundle.getBundle(“com.northconcepts.exception.example.exceptions”);
return bundle.getString(key);
}
}
3. 為列舉新增錯誤值
在很多時候可以為異常新增錯誤值,比如HTTP傳回值。這種情況下,可以在ErrorCode介面新增一個getNumber方法併在每個列舉中實現這個方法。
public enum PaymentCode implements ErrorCode {
SERVICE_TIMEOUT(101),
CREDIT_CARD_EXPIRED(102),
AMOUNT_TOO_HIGH(103),
INSUFFICIENT_FUNDS(104);
private final int number;
private PaymentCode(int number) {
this.number = number;
}
@Override
public int getNumber() {
return number;
}
}
新增錯誤碼可以是全域性數值也可以每個列舉自己負責。你可以直接使用列舉裡的ordinal()方法或者從檔案或資料庫載入。
4. 為異常新增動態屬性
好的異常處理還應該記錄相關資料而不僅僅是堆疊資訊,這樣可以在診斷錯誤和重現錯誤時節省大量時間。使用者不會在你的應用停止工作時告訴你他們到底做了什麼。
最簡單的辦法是給異常新增一個java.util.Map欄位。新欄位的職責就是透過名字儲存相關資料。透過新增setter方法可以遵循流式介面。
可以像下麵示例這樣新增相關資料並丟擲異常:
throw new SystemException(ValidationCode.VALUE_TOO_SHORT)
.set(“field”, field)
.set(“value”, value)
.set(“min-length”, MIN_LENGTH);
5. 避免不必要的巢狀
冗長的堆疊資訊不會有任何幫助,更糟糕的是會浪費你的時間和資源。重新丟擲異常時呼叫靜態函式而不是異常建構式。封裝的靜態函式決定什麼時候巢狀異常什麼時候只要傳回原來的實體。
public static SystemException wrap(Throwable exception, ErrorCode errorCode) {
if (exception instanceof SystemException) {
SystemException se = (SystemException)exception;
if (errorCode != null && errorCode != se.getErrorCode()) {
return new SystemException(exception.getMessage(), exception, errorCode);
}
return se;
} else {
return new SystemException(exception.getMessage(), exception, errorCode);
}
}
public static SystemException wrap(Throwable exception) {
return wrap(exception, null);
}
Your new code for rethrowing exceptions will look like the following.
catch (IOException e) {
throw SystemException.wrap(e).set(“fileName”, fileName);
}
6. 使用帶Web支援的集中式logger
再額外附贈一個建議。可能你情況很難向產品記錄日誌,這個麻煩可能來自多個中間商(很多開發者不能直接訪問產品環境)。
在多伺服器環境下情況可能會更糟。找到正確的伺服器或者確定問題影響到了哪個伺服器是一件非常令人頭痛的事情。
我的建議是:
-
將你的日誌記錄到一個地方,推薦記錄到資料庫中。
-
透過Web瀏覽器訪問資料庫。
有很多方法和備選產品可以達成這一標的,log collector、遠端logger、JMX agent、系統監視軟體等。甚至可以自己寫一個。重要的是要快速行動,一旦你達成了標的,你就可以:
-
幾秒鐘之內定位錯誤
-
為每個異常增加一個URL,可以記錄或者傳送email
-
讓你的夥伴可以在沒有你的情況下定位錯誤原因
-
避免測試人員為同一個bug新增多個記錄。他們可以在bug記錄裡增加一條異常URL
-
省錢
-
讓你的週末和名譽不受影響
你有什麼好的建議嗎?
希望這些建議對你有所幫助。給異常新增正確的資訊和將異常放在易於訪問的地方可以避免很多災難事故和時間浪費。如果你有一些自己的異常處理秘訣,歡迎分享。
下載
這裡包含了本文的所有程式碼(包括Eclipse專案)。程式碼的釋出遵循Apache 2.0協議。
http://northconcepts.com/blog-downloads/exceptions/NorthConcepts-Exceptions.zip
祝程式設計快樂!
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,看技術乾貨