不知道什麼時候開始,總是會有人問我什麼時候該用例外(Exception)這個功能,我們在許多程式語言裡面會發現不同的設計哲學。有的認為不需要,只要能回傳錯誤碼(Error Code)就足夠,有的則用豐富且強大的例外處理機制。
同時,我們很多時候在開發軟體的過程中不會去考慮例外、設置例外,進而讓這個隨時都可能發生的事情有點熟悉又很陌生。
預期的事情
既然是例外,就表示出現了我們預期以外的事情,就這點而言這個用詞跟翻譯都非常的精確。假設我們認為這段程式執行的時後不應該出現這個情況,那麼它就必須是一個例外。同時,當我們遭遇例外的時候,知不知道如何對應這個例外,就影響來我們是否用救援(rescue)或者捕捉(catch)的方式去處理。
以車禍的情況來說,我們會假設我們在台灣的路上是不會發生車禍的,這也是平常我們預設程式運行的狀況。然而,總是會有發生車禍的時候,如果我們不處理這個車禍,他可能就會一直被傳遞到警察、法官最後變得難以解決。
或者,我們也可以趕快進行處理呼叫道路救援(rescue)來協助我們,最後可能就可以用和解的方式收場。因此在程式中也是相同的,發生例外的嚴重程度取決於我們是否知道該如何「處理」以及如何「補救」這兩個處理方針。
框架中的設計
在 Ruby on Rails 這個框架中,儲存資料會提供 #save 和 #save! 兩種呼叫方式,前者預期會失敗因此出錯時就會回傳否定(false)的結果,後者預期「必定成功」因此失敗時會拋出沒有儲存(RecordNotSaved)的錯誤出來。透過這樣的方式,我們就可以很輕鬆地明白當時撰寫者的認為的情境(Context)是什麼。
同樣的,在查詢資料時我們會使用 #find 這個方法,然而卻沒有 #find! 這個方法,這是因為使用 #find 時是以主鍵(Primary Key)作為前提查詢的,如同我們的身分證字號一般。假設找不到這筆資料,那肯定會是一種例外,而不會是可預期的情況。
能夠以驚嘆號命名方法是 Ruby 語言的特性,透過這樣的小細節,就讓一段程式碼的意圖清晰很多,這也是我喜歡 Ruby 語言的理由之一。
善用例外
很多時候,我們在設計軟體都是以「一切完美」的前提去設計的,因此忽略了不完美的情況。然而,這些不完美的情況也不是我們能夠掌控的,就如同我們無法預知使用者是否會突然斷線一樣。
因此,何時該定義、使用例外,就是在那些我們不知道該如何「補救」的時候,大多數時候我們應該在「預期外的行為」以及「其他程式的例外」兩種情況拋出例外,像是網路斷線時我們可以避免直接拋出斷線,而是改為「無法讀取」的錯誤,讓資訊更為清晰,其他呼叫者也能更精確的定位問題來決定如何補救。