1. 概要(Overview)
Change Reference to Value は、参照オブジェクトとして扱っているものを、
値オブジェクト として扱うように変更するリファクタリングです。
参照オブジェクトは同一性を維持するのに便利ですが、常に一意なインスタンス管理が必要になります。
しかし、あるオブジェクトが 不変で同一性を気にしなくてもよい 場合は、値オブジェクトにした方がシンプルで安全です。
目的は以下の通りです:
- 不要な参照管理を取り除き、設計をシンプルにする
- 不変性を保証して、バグを減らす
- 値として比較できるようにして、テストや利用を簡単にする
2. 適用シーン(When to Use)
- オブジェクトが 不変 である(例:金額、座標、日付、住所など)
- 同一性を維持する必要がない
- 値が同じならオブジェクトも等しいとみなして問題ない
- 現状の参照オブジェクト管理が冗長で、複雑さを生んでいる
よくある匂い:
- Needless Indirection(不要な間接化)
- Complex Class(複雑すぎるクラス設計)
3. 手順(Mechanics / Steps)
- 参照オブジェクトを特定
- そのオブジェクトを 不変の data class に書き換える
-
equals
/hashCode
による値比較を利用できるようにする - インスタンス管理用のキャッシュやリポジトリを削除
- クライアントコードを修正して値オブジェクトとして利用
4. Kotlin 例(Before → After)
Before:参照オブジェクトとして管理している
// 参照オブジェクト
class Currency private constructor(val code: String) {
companion object {
private val instances = mutableMapOf<String, Currency>()
fun get(code: String): Currency {
return instances.getOrPut(code) { Currency(code) }
}
}
}
fun main() {
val usd1 = Currency.get("USD")
val usd2 = Currency.get("USD")
println(usd1 === usd2) // true (常に同じインスタンスを返す)
}
-
Currency
を参照オブジェクトとして管理 - しかし
USD
などの通貨コードは不変で、同一性を管理する必要はない
After:値オブジェクトに変更
// 値オブジェクト
data class Currency(val code: String)
fun main() {
val usd1 = Currency("USD")
val usd2 = Currency("USD")
println(usd1 == usd2) // true (値が同じだから等しい)
println(usd1 === usd2) // false (別インスタンスでも問題なし)
}
-
data class
に変更することで値比較が可能 - 「USD」は不変で意味が一貫するため、複数インスタンスでも矛盾はない
- 不要なキャッシュやインスタンス管理がなくなりシンプルに
5. 効果(Benefits)
- 不変性を保証できる → 副作用が減る
- インスタンス管理が不要になり、設計がシンプル化
- 値比較が簡単になる(
==
で自然に比較可能) - テストや利用が楽になる
6. 注意点(Pitfalls)
- 本当に同一性が不要かを慎重に判断すること
- 値オブジェクトは不変であるべき → フィールドは基本的に
val
にする - 将来的に「状態を持つ可能性」があるなら参照オブジェクトのままがよい
まとめ
- Change Reference to Value は「参照オブジェクトを値オブジェクトに変える」リファクタリング
- 判断基準:このオブジェクトは不変であり、同一性を管理する必要がないか?
- 基本思想:不変なデータは値として扱う方がシンプルで安全