以下的內容需要先閱讀『敏捷式例外處理設計的第一步:決定例外處理等級』比較容易理解。
今天 Teddy 要談一個導致程式無法達到 G1 (error-reporting) 例外處理等級的 exception handling bad smell (例外處理壞味道): dummy handler 。先看一個 Java 程式片段的例子:
public void doIt(){
try {
// Do IO operations
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} // TODO Auto-generated catch block
e.printStackTrace();
}
寫過 Java 程式的鄉民們,應該對上面這個 catch clause 裡面的程式碼不陌生,這就是一個 dummy handler。所謂的 dummy handler,就跟政客的競選政見一樣,聽(看)起來頭頭是道,實際上卻是空無一物,看不到牛肉。e.printStackTrace(); 這一行程式,事實上並沒有實質地 handle (處理) 例外,只是單純把例外印到 console 中。這樣的訊息通常只有 programmers 在程式開發的時候才看得到,對於這個 method 的呼叫者 (caller) 而言當例外發生之後並不會得到任何通知,因此會以為該 method 的執行沒有問題而繼續執行下去,但實際上系統的狀態可能已經發生了錯誤了。
Dummy handler 還會讓 programmers 產生一種『我已經把裡外處理好』的錯覺,實際上這樣的例外處理卻連 G1 等級都沒有達到。
*******
Replace Dummy Handler with Rethrow
要移除 dummy handler 這個 bad smell 很簡單,只要套用 Replace Dummy Handler with Rethrow 這個 refactoring 就可以達到 G1 例外處理等級,請看:
public void doIt(){
try {
// Do IO operations
}
catch (IOException e) {
throw new UnhandledException(“message”, e);
}
} throw new UnhandledException(“message”, e);
}
附註說明一下,其中 UnhandledException 是一個在其他地方事先定義好的 RuntimeException。另外,在 main program 中需要有一個超大的 try block 用來捕捉所有的例外並回報個使用者知道,如此便可達到 G1。
如果上面的程式要達到 G2 或是 G3 還有其他相對應的方法,改天有空再談。
*******
以下為 QA 時間。
鄉民甲:為什麼不直接把 IOException 丟出去,還要轉成另一個 RuntimeException?
Teddy 回答:G1 的目的只是為了讓例外可以被回報,也就是保證沒有例外會被隱藏或忽略。由於 IOException 是一個 checked exception,所以如果只是為了『回報例外』這個目的,而直接把它往外丟,那麼就需要修改 doIt() 的介面如下:
public void doIt() throws IOException;
而呼叫 doIt() 的人也需要修改,造成所謂的漣波效應。為了避免這個問題,才使用一個 RuntimeException 來回報錯誤。
Teddy 回答:應該說在 Java 程式中很容易出現,而其他語言也『不排除』會有這個 smell (鄉民乙:講得很心虛喔...)。由於絕大多數的程式語言只有 unchecked exceptions (也就是 runtime exceptions) ,因此 programmers 沒有必要為了符合類似 Java 所規定的 catch or declare rule 而需要去捕捉這些 unchecked exceptions,因此預設情況下所有未被捕捉的 unchecked exceptions 直接就往上層傳遞 (propagate) ,而自動達到 G1(記得在程式最外層加上一個大的 try block)。
友藏內心獨白:看得懂 Teddy 在講什麼東東的人請到前面來領糖果...
沒有留言:
張貼留言