1. 概要(Overview)
Introduce Null Object は、null を返したりチェックする代わりに、
「何もしない安全なオブジェクト」を導入するリファクタリングです。
null チェックが散在すると、コードが複雑になり、バグ(NullPointerException)の原因にもなります。
その代わりに、振る舞いは最小限だがインターフェースを満たすオブジェクト(Null Object)を用意することで、
クライアント側の null 判定を不要にできます。
目的は以下の通りです:
-
nullチェックの削減 - コードを読みやすく、安全にする
- 既存クラスと同じインターフェースで「空の振る舞い」を提供
2. 適用シーン(When to Use)
-
nullチェックがあちこちに散らばっている - 「デフォルト動作が何もない」ケースが多い
- クライアントコードで
if (obj != null)のようなガードが頻発している - 例外やエラーではなく「何もしない」で十分な場面がある
よくある匂い:
- Repeated Null Checks(繰り返される null チェック)
- Primitive Obsession(プリミティブ執着、null で状態を表現している)
3. 手順(Mechanics / Steps)
-
nullを返している箇所を特定する - 返り値の型のインターフェースを持つ Null Object クラス を作成
- Null Object に「何もしない実装」を追加
- 元の
nullを返す箇所を Null Object に置き換える - クライアントコードから
nullチェックを削除する
4. Kotlin 例(Before → After)
Before:null を返していて、毎回チェックが必要
interface Customer {
fun getName(): String
}
class RealCustomer(private val name: String) : Customer {
override fun getName(): String = name
}
class CustomerRepository {
private val customers = listOf(RealCustomer("Alice"))
fun findByName(name: String): Customer? {
return customers.find { it.getName() == name }
}
}
fun main() {
val repo = CustomerRepository()
val customer = repo.findByName("Bob")
if (customer != null) {
println("Found: ${customer.getName()}")
} else {
println("No customer found")
}
}
-
findByNameがnullを返す可能性がある - クライアント側で毎回
if (customer != null)を書く必要がある
After:Null Object を導入
interface Customer {
fun getName(): String
}
class RealCustomer(private val name: String) : Customer {
override fun getName(): String = name
}
object NullCustomer : Customer {
override fun getName(): String = "No customer"
}
class CustomerRepository {
private val customers = listOf(RealCustomer("Alice"))
fun findByName(name: String): Customer {
return customers.find { it.getName() == name } ?: NullCustomer
}
}
fun main() {
val repo = CustomerRepository()
val customer = repo.findByName("Bob")
println("Found: ${customer.getName()}")
}
-
NullCustomerが「何もしない安全なオブジェクト」 - クライアント側で
nullチェック不要 - コードがシンプルになり、安全性も向上
5. 効果(Benefits)
- NullPointerException のリスクを削減
-
nullチェックを削除でき、コードがスッキリする - クライアントコードが安全で読みやすくなる
- テストもしやすくなる(Null Object をそのまま使える)
6. 注意点(Pitfalls)
- 「null を返すこと自体がエラー」のケースでは適用不適切(例:必須項目が存在しない場合)
- Null Object の振る舞いを明確に定義しておく必要がある(例:ログを出すのか、完全に無視するのか)
- 過度に使うと「バグを隠す」危険もある(例:本当はエラーなのに黙って Null Object が返る)
まとめ
-
Introduce Null Object は「
nullの代わりに安全なダミーオブジェクトを導入する」リファクタリング - 判断基準:そのケースで「何もしない」振る舞いが妥当か?
-
基本思想:
nullを返さず、オブジェクトとして振る舞うことでコードを安全かつシンプルにする