簡體   English   中英

拋出一長串異常vs拋出異常vs拋出自定義異常?

[英]Throw a long list of exceptions vs throw an Exception vs throw custom exception?

我有一個使用兩種API方法的應用程序。 這兩種方法都拋出超過五個例外。 所以,如果我只是添加一個throws聲明,那么它將成為一個超過十個的列表。 我的方法不能處理十個例外中的任何一個

我讀過,拋出一長串異常是一種不好的做法。 拋出(傘)異常是一種不好的做法。 所以我該怎么做?

  1. 添加一個try catch塊,並在catch塊中記錄並退出?
  2. 創建自定義異常類,包裝每個異常並拋出自定義異常?
  3. 為所有例外添加投票聲明?
  4. 拋出異常?
  5. 添加一個try catch塊,並在catch塊中拋出RuntimeException? (目前的做法)

編輯:添加選項5。

“2.創建自定義異常類”,但不是全部。 在邏輯組中包裝異常。 例如,您可以擁有XmlWritingExceptionPaymentGatewayExceptionDataAccessException ,它們根據場景包裝不同的異常。

甚至可以(並且首選)將相同的異常包裝在不同的包裝器中。 例如,如果付款由於通信問題而失敗,則可以在PaymentGatewayException包裝IOException ,但如果在使用xml進行某些i / o操作期間失敗則在XmlWritingException 所有這些例子都是假設的,但你明白了。

最重要的是 - 將原始異常設置為新異常的原因,這樣它就不會丟失。

更新:事實上,如果你不能指望客戶從合理的異常中恢復,那么選項5就可以了。 更好的是 - 您創建的自定義異常可以擴展RuntimeException 這就是spring所做的事情,例如,在DataAccessException包裝所有與數據相關的異常。

如果你的代碼的調用者可以想象處理一些異常,那么我會聲明你的方法拋出它們並傳遞它們。 如果沒有希望,那么我將創建自己的自定義異常,擴展(取消選中)RuntimeException,並拋出自定義異常,並將實際異常鏈接到其中。

我一般會推遲決定退出到盡可能高的政策。 退出(如果您的意思是退出流程)應始終在代碼中推遲,以便您可以對其進行單元測試。

一般來說,我對例外的理念是:

  1. 如果錯誤是“業務邏輯問題”並且調用者可以處理它(通過選擇備用邏輯或詢問用戶要做什么),則拋出自定義檢查的異常。 示例:拋出UserDoesNotExistException以指示數據系統中不存在用戶名,並讓下一層請求用戶創建帳戶。 當你處於它的時候,你可能想要考慮代碼是否可以以一種實際上不需要異常的方式構建。
  2. 意外的異常應該是運行時(未檢查)異常。 您希望它們在很大程度上保持不變的代碼(線程堆棧很小),將它們作為異常條件處理(通過拋出錯誤頁面,記錄錯誤,向操作員發送警報,內省)系統的狀態,包括錯誤報告等)。 不要捕獲,包裝和重新拋出,除非您可以添加可用於診斷問題的實際信息。

一般來說,如果你的方法無法處理異常,它應該重新拋出它們或將它們添加到throws部分。 隱藏catch塊中的異常似乎是最糟糕的選擇。

一如既往,這取決於。

  1. 這是你的API嗎? 如果是,則更改它,讓它拋出更少的異常。 以某種方式處理它們。

  2. 盡量讓你的程序盡可能長時間工作。 這意味着,如果沒有配置文件,請不要拋出IOException並退出。 硬編碼一些默認配置。 如果沒有互聯網連接,請顯示該消息,不要退出。

  3. 有時候,可以返回一些特殊的東西(特殊情況模式和Null對象模式的變體)而不是拋出異常。

  4. 考慮針對某些特殊情況實施Observer模式。 不要拋出異常,但要通知應該顯示消息或執行其他操作的人。

  5. 不要隱藏任何一個。 記錄所有異常。

如果您的API名為Foo,我會創建一個名為FooAPIException的異常。 確保在FooAPIException中嵌入源異常。 記錄和顯示FooAPIException時,還會顯示源異常的堆棧跟蹤。

例如:

public class FooAPIException extends Exception {
    private Exception root;

    public FooAPIException (Exception e) {
        super(e.getMessage(),e.getErrorCode());
        root = e;
    }

    public Exception getRoot () {
        return root;
    }

// Exception ================================================================
    public String toString () {
        StringBuffer sb = new StringBuffer();

        sb.append("[");
        sb.append(getErrorCode());
        sb.append("][");
        sb.append(getMessage());
        sb.append("]\n");
        sb.append("ROOT CAUSE:");

        Writer write = new StringWriter();
        PrintWriter pw = new PrintWriter(write);
        e.printStackTrace(pw);
        pw.close();
        try {
            write.close();
        } catch (IOException ioe) {/**/}
        root = write.toString();
        sb.append(write);

        return sb.toString();
    }   
}

然后包裝API:

public class FooAPI {
    public static method (String params) throws FooAPIException {
        try {
            RealFooAPI.method(params)
        } catch (Exception e) {
            throw new FooAPIException(e);
        }
    }
}

您可以使用一些自定義異常包裝異常。

但問題是,通過隱藏原因異常(例如,它是IOException ),您隱藏了理論上允許更高級別的代碼專門捕獲該異常並處理它的信息。 在這種情況下,你最好不要拋出RuntimeException ,所以你也可以用RuntimeException包裝它們。

我仍然會留下那些你可以想象的合理的來電者,他們希望專門捕捉並專門宣布它們,或者將它們分組。

如果您沒有在該方法中處理異常,最好將它們包裝到一個或多個自定義異常中並拋出它們,以便您可以在上層處理它們。

您還可以創建自定義未經檢查(擴展的RuntimeException)異常,並將拋出的異常包裝到自定義運行時異常中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM