歡迎光臨
每天分享高質量文章

改進異常處理的 6 條建議

(點擊上方公眾號,可快速關註)


來源:ImportNew – 唐尤華 ,

合理地使用異常處理可以幫你節省數小時(甚至數天)除錯時間。一個乘法異常會毀掉你的晚餐乃至周末計劃。如果處置不及時,甚至對你的名譽都會造成影響。一個清晰的異常處理策略可以助你節省診斷、重現和問題糾正時間。下麵是6條異常處理建議。

1. 使用一個系統全域性異常類

不必為每種異常型別建立單獨的類,一個就夠了。確保這個異常類繼承RuntimeException,這樣可以減少類個數並且移除不必要的異常宣告。

我知道你正在想什麼:如果型別只有一個,那麼怎麼能知道異常具體是什麼?我將如何追蹤具體的屬性?請繼續閱讀。

2. 使用列舉錯誤碼

我們大多被教授的方法是將異常轉為錯誤信息。這次查看日誌檔案時很好,(呃)但是這樣也有缺點:

  1. 錯誤信息不會被翻譯(除非你是Google)

  2. 錯誤信息不會轉換為用戶友好的文字

  3. 錯誤信息不能用編程的方式檢測

將異常訊息留給開發者定義也會出現同樣的錯誤有多種不同的描述。

一個更好的辦法是使用列舉表示異常型別。為每個錯誤分類創建一個列舉(付款、認證等),讓列舉實現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

再額外附贈一個建議。可能你情況很難向產品記錄日誌,這個麻煩可能來自多個中間商(很多開發者不能直接訪問產品環境)。

在多服務器環境下情況可能會更糟。找到正確的服務器或者確定問題影響到了哪個服務器是一件非常令人頭痛的事情。

我的建議是:

  1. 將你的日誌記錄到一個地方,推薦記錄到資料庫中。

  2. 通過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」,看技術乾貨

赞(0)

分享創造快樂