除非萬不得已,別 Catch!

【編者按】做者 Yegor Bugayenko 是 Teamed.io 的軟件架構師,熱衷於軟件質量研究和有效的項目管理方法探索。在本文中,Yegor 就**「異常被捕獲但並未從新拋出」**這個問題進行了深刻討論,並分享了一些建議。html

對異常只捕獲但並未從新拋出到底是 anti-pattern,仍是個普通並且很是流行的錯誤確實無從考究。但毫無疑問的是,在全部異常捕獲代碼中,它基本無處不在,正以下面這段代碼:java

try {
	  stream.write(data);
	} catch (IOException ex) {
	  ex.printStackTrace();
    }

©Catch Me If You Can (2002) by Steven Spielberg 注意:下面的代碼並無反對。編程

try {
	  stream.write('X');
	} catch (IOException ex) {
	  throw new IllegalStateException(ex);
	}

這叫作 exception chaining,是一個很是有效的構造。架構

那麼,捕獲異常並記錄究竟存在什麼樣的問題?首先,從宏觀着手,這裏正在談論的是面向對象編程——意味着須要處理的是對象。一個對象(準確的說,是它的類)應該是這個樣子:性能

final class Wire {
      private final OutputStream stream;
      Wire(final OutputStream stm) {
       this.stream = stm;
     }
     public void send(final int data) {
        try {
          this.stream.write(x);
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }

這裏這樣來應用這個類的:this

new Wire(stream).send(1);

看起來不錯,對麼?當調用 send(1) 時,並不須要擔憂出現 IOException ,它將在方法內部處理。同時,若是出現異常,異常信息會被記錄。可是這麼作的理念是徹底錯誤的,它傳承自沒有異常處理的語言,好比 C。設計

異常的發明是爲了將整個錯誤處理代碼從主要邏輯中移除,以此來簡化設計。同時,它們不只僅是被移走,並且被集中在一個地方——在 main() 方法中,即整個應用的入口。code

一個異常的主要目的是蒐集儘量多的錯誤信息並將它拋到最上層,在這裏用戶可以針對它作一些處理。Exception chaining 則能夠幫助更多,它容許在異常拋至上層的過程當中擴充錯誤信息。在這個過程當中,實際上很是相似於每次捕獲到泡泡(即異常)後,所作的只是將它添加到一個更大的泡泡中而後從新拋出。當它到達最高層的時候,那裏就有許多泡泡,像一個 Russian Doll,一個嵌套着另一個。最原始的異常就是最小的那個泡泡。htm

當捕獲一個異常而並無從新拋出時,等同於你捏碎了這個泡泡。其中包含的大量信息,包括最原始的異常和全部其它帶有信息的泡泡,都被你緊緊的抓在手中。你杜絕爲上層呈現它,同時你如何處理和使用上層也毫無察覺。這一切都像是暗箱操做,一些潛在的重要信息被隱藏。對象

所以,在這裏直接致使的是 send() 方法沒法獲得信任,一樣基於 send() 方法的操做也沒法獲得信任,對象之間的信任鏈被破壞殆盡。這裏的建議是儘量少捕獲異常,同時一旦捕獲則必須拋出。

不幸的是,Java 在不少地方的設計違背了這個原則。例如,Java 有需檢查和不需檢查的異常兩類,可是在我看來,只應該有需檢查的異常(這些異常必須被捕獲或者聲明爲 throwable)。並且,Java 容許在一個方法中將多個異常類型聲明爲 throwable ——這是另外一個錯誤;堅持只聲明一種類型。在層次結構的頂部有一個通用的 Exception 類,在我看來也是錯誤的。除此以外,一些內置的類不容許拋出任何需檢查的異常,好比說Runnable.run()Java 還有許多關於異常的問題。

可是嘗試記住這些原則,你的代碼將會更加整潔:catch 除非你別無選擇。

P.S.這個類應該是這個樣子:

final class Wire {
      private final OutputStream stream;
      Wire(final OutputStream stm) {
        this.stream = stm;
      }
      public void send(final int data)
        throws IOException {
        this.stream.write(x);
      }
    }

原文連接:Catch Me If You ... Can't Do Otherwise

本文系 OneAPM 工程師編譯整理。OneAPM 是應用性能管理領域的新興領軍企業,能幫助企業用戶和開發者輕鬆實現:緩慢的程序代碼和 SQL 語句的實時抓取。想閱讀更多技術文章,請訪問 OneAPM 官方博客

相關文章
相關標籤/搜索