l

2013年12月24日 星期二

例外處理實務做法(2上):Expressive Exception Interface、Throwing Server、 Checked Server Problem、Homogeneous Exception、Smart Exception

Dec. 23 10:18~12:03

螢幕快照 2013-12-23 上午11.28.22

圖片來源在此

 

今天介紹Arno Haase在《Java Idioms: Exception Handling》這篇文章中所介紹的幾個針對Java語言的例外處理實務做法,文章可在此下載。Haase的文章採用pattern的格式來撰寫,Teddy將以簡短的說明來解釋這些pattern的用意。

***

Expressive Exception Interface

例外的產生,造成丟出例外的人,以及接收例外的人之間的隱性耦合(hidden coupling)。寫過程式的鄉民們應該都知道,耦合關係如果處理不好,會造成程式難以理解、修改、擴充。由於例外地傳遞具有「非區域性(non-local)」的特性,也就是會沿著call chain的路徑一直往上傳遞,如果沒有妥善的規劃,很可能在執行期間造成不預期的錯誤狀況。

因此,將例外視為介面的一部分,不僅應該宣告在函數(function、method)之上,還應該讓使用者知道,整個類別或是package會產生那些例外。並且在設計軟體架構的時候,將例外處理的問題列入考慮,訂定系統面的例外處理策略。

***

Throwing Server

一個函數的執行發生了問題,但是這個問題無法在函數內部被解決,該如何處理?

用例外來通知客戶端程式,該函數的執行失效。如果執行失效的問題是由客戶端所產生,例如傳入不正確的參數,使用Unchecked Client Problem,否則使用Checked Server Problem。

***

Checked Server Problem

使用checked exception來表示一個Throwing Server(一個函數或是軟體元件)的執行結果為失效(failure)。其失效原因為該Throwing Server內部實做遇到錯誤狀況,而非因為無效的輸入所引起。

***

Homogeneous Exception

如果每一個函數都套用了Checked Server Problem這個模式,那麼一個函數的介面可能會有很多不同的checked exception,加總起來某一個類別的全部函數所丟出的例外種類將會非常的驚人。

因此,產生一個新的例外類別,用這個例外類別來代表同一個類別所有函數可能會丟出的例外。這種例外類別稱之為同質性例外(homogeneous exception)。例如,一個ATM類別的login()、withdraw()、deposit()函數可能會遇到IOException、SQLException:

login() throws IOException, SQLException

withdraw() throws IOException, SQLException

deposit() throws IOException, SQLException

鄉民民可能會想,上述作法違反了昨天〈例外處理實務做法(1)〉所建議的第4點「將低階層的例外轉成高階層所理解的例外」,直接暴露了實做細節的例外。因此,改成:

login() throws loginException

withdraw() throws WithdrawException

deposit() throws DepositException

修改後的版本雖然符合了「將低階層的例外轉成高階層所理解的例外」這條建議,但是卻可能產生過多例外類別,違反了「避免宣告很多例外類別」這條原則。如果鄉民們並不希望一個ATM類別的不同函數各自丟出不一樣的例外,則可套用Homogeneous Exception模式來解決這個問題:

login() throws ATMOperationException

withdraw() throws ATMOperationException

deposit() throws ATMOperationException

然後將將產生裡外的原因串接到ATMOperationException身上,以便接收到例外的人可以有足夠的脈絡資源可以進一步地進行處理。

***

Smart Exception

客戶端程式捕捉到例外之後,有時候需要依據例外發生原因而設計不同的例外處理方式。例如當你遇到LoginException這個例外,如果是帳號不存在,你會問使用者是否要新增帳號。如果是密碼錯誤,你會顯示重新登入或是寄發密碼的選項。要如何讓捕捉到例外的人可以進一步依據例外發生的原因,來設計不同的處理方式?

設計一個列舉類別(enumeration class)來代表錯誤發生的原因。在例外類別之中包含將這個例舉類別,使得例外物件變得「聰明」,讓客戶端程式可以直接透過以下方式來依據不同錯誤原因執行不同的處理方式:

螢幕快照 2013-12-23 上午11.47.25

圖片來源在此

***

最後總結一下今天介紹的幾個例外處理pattern:

  • Expressive Exception Interface:和Teddy之前介紹過的〈為什麼例外處理那麼難(2):設計觀點〉要強調的內容一致。和昨天介紹的「盡可能在接近問題發生處來處理例外」與「將例外處理責任指派給可以做決定的物件」這兩個例外處理實務做法相關。
  • Throwing Server:和「僅使用例外來發出緊急事件」相關。
  • Checked Server Problem:這是Java對於checked exception使用的另一種表示方法,類似Teddy之前介紹過的〈Fault、Error、Failure、Exception〉,用checked exception來代表某個函數執行結果為failure。
  • Homogeneous Exception:和昨天介紹的「避免宣告很多例外類別」、
    「將低階層的例外轉成高階層所理解的例外」、「用哪裡出了問題幫例外命名,而不是用誰丟出了例外來命名」有點相關,都是用來告訴鄉民們如何設計例外類別。。
  • Smart Exception:可以視為「伴隨著例外物件提供足夠的脈絡資訊」以及〈例外處理的四種Context(1):Exception Context〉的一種實作範例,讓例外物件夾帶更多的脈絡資訊,以方便捕捉到例外的人設計例外處理策略。

這篇文章的內容有點多,剩下的pattern下集繼續介紹。

***

友藏內心獨白:Homogeneous Exception還蠻常用的。

沒有留言:

張貼留言