1. 概要(Overview)
Replace Error Code with Exception は、メソッドの失敗を 戻り値(エラーコード) で表現する代わりに、
例外(Exception)をスローして通知するように変更するリファクタリングです。
エラーコード方式だと:
- 呼び出し側が
if (result == -1)のようなチェックを必ず書く必要がある - チェック漏れでバグを招きやすい
- 戻り値が「成功値」と「エラー値」の両方に使われてしまう
例外に置き換えることで、エラー処理を明示的に分離でき、コードがシンプルになります。
2. 適用シーン(When to Use)
- メソッドが 特殊な値(-1, null, error code) を返してエラーを表している
- 呼び出し側でエラー判定が散在している
- 「エラー処理」と「通常処理」が混ざって読みにくい
よくある匂い:
- Error Handling Through Special Return Values(特殊値によるエラー処理)
- Duplicated Conditional Logic(重複した条件分岐)
3. 手順(Mechanics / Steps)
- エラーを返す代わりに 例外クラスを定義
- メソッド内でエラー発生時に 例外をスロー
- 呼び出し側を修正し、戻り値チェック → try/catch に置き換え
- テストで正常系・異常系を確認
4. Kotlin 例(Before → After)
Before:エラーコードを返す
class Account(var balance: Int) {
fun withdraw(amount: Int): Int {
return if (amount > balance) {
-1 // エラーコード
} else {
balance -= amount
balance
}
}
}
fun main() {
val account = Account(100)
val result = account.withdraw(150)
if (result == -1) {
println("Insufficient funds")
} else {
println("New balance: $result")
}
}
After:例外に置き換える
class InsufficientFundsException(message: String) : Exception(message)
class Account(var balance: Int) {
fun withdraw(amount: Int): Int {
if (amount > balance) {
throw InsufficientFundsException("Insufficient funds: $amount > $balance")
}
balance -= amount
return balance
}
}
fun main() {
val account = Account(100)
try {
val newBalance = account.withdraw(150)
println("New balance: $newBalance")
} catch (e: InsufficientFundsException) {
println("Error: ${e.message}")
}
}
- エラーコード
-1を廃止し、InsufficientFundsExceptionをスロー - 呼び出し側で
try/catchによってエラー処理を分離できる
5. 効果(Benefits)
- 通常処理とエラー処理を明確に分離できる
- エラー処理の漏れを防ぎやすい(例外が伝播して検出される)
- 戻り値を 本来の意味(成功値) に専念させられる
- 呼び出しコードの可読性が向上
6. 注意点(Pitfalls)
-
例外は「異常系」専用 → 「普通に起こりうる分岐」には使わない
- 例:検索結果が存在しない →
nullやOptionalの方が適切 - 本当に「異常な状態(不正、資源不足)」なら例外
- 例:検索結果が存在しない →
- 過度な例外使用はパフォーマンスや可読性を下げる
- 公開 API では「どの例外をスローしうるか」を明示すべき
まとめ
- Replace Error Code with Exception は「特殊な戻り値でのエラー通知」をやめて、例外スローで明示化するリファクタリング
- 判断基準:そのエラーは「正常系の一部」か「本当に異常」か?
-
基本思想:
- 通常の制御フロー → 戻り値
- 予期しないエラーや失敗 → 例外