iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0
Kotlin

讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern系列 第 9

D09: 當回傳值可能有副作用時,回傳 null 或封裝後的 Result Type

  • 分享至 

  • xImage
  •  

主題 Effective Kotiln Item 7: Prefer null or Failure result when the lack of a result is possible

有時一個 function 不一定能產生它期望的結果,而且這個錯誤是可以預判的,通常稱作副作用,以下是一些常見的例子:

  • 把一個數字除以 0
  • 試圖從 Collection 取出第 3 個元素,但 Collection
  • 嘗試從 JSON 解析一個物件,但這段 JSON 格式錯

處理這種情況有兩種主要機制:

  • 回傳一個 null 或一個封裝類(通常被命名為 Failure,Error),從而表示失敗。
  • 拋出異常 (throw Exception)

這兩者之間存在一個重要的區別。異常不應該被用作標準的傳遞信息方式。所有的異常都表示特別的、特殊的情況。應該在預期不到的狀況才使用。 Effective Java 也說到 Use Exceptions for Only Exceptional Circumstances。

Throw Exception 的危險性

  • Exception 的流程很像是一種 goto,需要外面有地方接
  • 大多數開發者對於異常的傳播方式不太容易讀懂,且在程式碼中可能容易被遺漏
  • 在 Kotlin 中,所有的異常都是 unchecked 的。使用者不被強制或甚至不被鼓勵來處理它們
  • 將程式放在 try-catch 塊內會抑制編譯器可能執行的某些優化。

null 或 Failure Result Type

null 或 Failure 都非常適合於表示預期的錯誤,他代表著函式除了 Happy Path 外,也有某種錯誤的可能。這樣對開發者更明確、高效,且可以用預期的方式處理。這就是為什麼規則是:當我們預期出錯時,我們應該優先回傳 null 或 Failure,這表明了有發生異常的可能,讓開發者會處理他。以下範例

inline fun <reified T> String.readObjectOrNull(): T? {
    //...
    if (incorrectSign) {
        return null
    }
    //...
    return result
}

inline fun <reified T> String.readObject(): Result<T> {
    //...
    if (incorrectSign) {
        return Result.failure(JsonParsingException())
    }
    //...
    return Result.success(result)
}

class JsonParsingException : Exception()

參考影片

Yes

Sealed Result Type

即然有預期錯誤的可能,就應該在回傳型別表達出來。這時就需要 Result Type, 如果你有聽過 Either,那跟 Result type 的概念相同。雖然書中介紹自已設計一個 Result Type, 但我們沒有要這麼 Hardcore, 就找了個 Kotlin-Result (Github 826☆)

這裡是用正常是 OK<V> 錯誤是 Err<V> 並封在 Result 這個 Type

fun checkPrivileges(user: User, command: Command): Result<Command, CommandError> {
    return if (user.rank >= command.mininimumRank) {
        Ok(command)
    } else {
        Err(CommandError.InsufficientRank(command.name))
    }
}

或是直接 Catch Db Exception 轉成 Err<Throwable>

val result: Result<Customer, Throwable> = runCatching {
    customerDb.findById(id = 50) // could throw SQLException or similar
}

如果一來回傳值就代表了我們預期的所有可能性,開發者雖然不喜歡,但也會被強迫的要去處理這樣的副作用。

補充 Kotlin 的 Result Type

編按: Kotlin 在 Effective Kotlin 推出時還沒有 Result type,不過即使時至今日,Kotlin 內建的 Result type 也不太常被使用。不太好使 (因為他不太像一個 Monad)

每日一推 (G)I-DLE

孩子們 YT 播放 1.4億次的 HANN
Yes


上一篇
D08 : 利用 require 與 check 與開發者訂定契約吧
下一篇
D10: 好的程式碼應該跟文章一樣,談程式碼的可讀性
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言