1. 概要(Overview)
Change Unidirectional Association to Bidirectional は、
一方のクラスからしか参照できなかった関連を、双方向で参照可能にするリファクタリングです。
目的は以下の通りです:
- 双方向のナビゲーションを可能にして、利便性を向上
- 関連するオブジェクトを相互にたどれるようにする
- モデルやドメインの表現力を高める
2. 適用シーン(When to Use)
- クラス A から B へはアクセスできるが、B から A へもアクセスしたい
- 双方向の関係を持たないとロジックが不自然になる
- クライアントコードが「A のリストを全部たどって B を探す」ような冗長な処理を書いている
例:
-
OrderはCustomerを参照しているが、Customer側からもOrderを見たい -
TeamはPlayerを持っているが、PlayerからTeamにアクセスしたい
3. 手順(Mechanics / Steps)
- 片方向の関連を確認(例:
Order → Customer) - 逆方向の関連を新しく追加(例:
Customer → Order) - 2つの関連を 同期させるメソッド を導入
- 双方向の整合性が崩れないようにテストを追加
4. Kotlin 例(Before → After)
Before:一方向の関連
class Customer(val name: String)
class Order(val id: Int, val customer: Customer)
fun main() {
val customer = Customer("Alice")
val order = Order(101, customer)
println(order.customer.name) // ✅ OK
// println(customer.orders) // ❌ Customer から Order をたどれない
}
-
OrderからはCustomerを参照できる - しかし
Customer側からは自分の注文をたどれない
After:双方向の関連に変更
class Customer(val name: String) {
private val _orders = mutableListOf<Order>()
val orders: List<Order> get() = _orders
fun addOrder(order: Order) {
_orders.add(order)
if (order.customer != this) {
order.setCustomer(this) // 双方向の整合性を保つ
}
}
}
class Order(val id: Int, private var _customer: Customer? = null) {
val customer: Customer? get() = _customer
fun setCustomer(customer: Customer) {
_customer = customer
if (!customer.orders.contains(this)) {
customer.addOrder(this) // 双方向の整合性を保つ
}
}
}
fun main() {
val customer = Customer("Alice")
val order = Order(101)
order.setCustomer(customer)
println(order.customer?.name) // Alice
println(customer.orders.map { it.id }) // [101]
}
- 双方向にアクセス可能 (
order.customer,customer.orders) -
setCustomerとaddOrderで双方向の整合性を保証
5. 効果(Benefits)
- 双方向のナビゲーションが可能になり、コードがシンプルに
- モデルの表現力が高まる(現実世界に近い)
- 関連の整合性を内部で管理できる
6. 注意点(Pitfalls)
- 双方向の同期処理が増え、バグ(無限ループなど) を招く可能性がある
- 双方向にすると依存関係が強まり、結合度が高くなる
- 「本当に双方向が必要か?」を検討してから導入すること
- 片方向アクセスで十分ならそのままの方が安全
まとめ
- Change Unidirectional Association to Bidirectional は「一方向の関連を双方向に変える」リファクタリング
- 判断基準:その関連は本当に双方向にすべきか?
- 基本思想:ナビゲーションを簡単にする一方で、整合性維持の責務が増える