はじめに
不適切な親密さ(Inappropriate Intimacy) とは、
2つのクラスが互いの内部に 過度に依存・干渉 している状態を指します。
例:
- AクラスがBクラスの内部フィールドに直接アクセス(getter 連発など)
- 双方向参照があり、どちらかを変更するともう一方も必ず修正が必要
- 本来は疎結合であるべき層(Domain ↔ Infrastructure)が強く結びついている
→ カプセル化違反 であり、テスト・保守・再利用を難しくします。
25.1 特徴
- クラスが他クラスの詳細に強く依存している
- getter/setter 経由で相手クラスのデータを頻繁に操作
- 双方向の参照が存在し、依存が複雑化
- 境界(レイヤーやモジュール)が崩壊している
25.2 解決手法
-
メソッド移動(Move Method)
→ 相手クラスのデータを頻繁に操作する処理は、利用している側ではなくそのクラスへ移動 -
フィールド移動(Move Field)
→ 本来属すべきクラスにフィールドを移動 -
仲介の導入(Introduce Mediator / Facade)
→ 双方向依存を避け、中間クラスを設ける -
依存方向の整理(Dependency Inversion, Clean Architecture)
→ 下位層 → 上位層への依存を逆転させ、境界を明確にする
25.3 Kotlin 例
Before:不適切な親密さ
class Customer(val name: String, val address: Address)
class Address(var street: String, var city: String) {
fun updateFromCustomer(customer: Customer) {
// Customer の情報に直接依存
println("Updating address for ${customer.name}")
}
}
-
AddressがCustomerの内部に依存 - 双方向に近い結合 → 修正時に影響が拡大
After①:責務を移動
class Customer(val name: String, val address: Address) {
fun updateAddress(street: String, city: String) {
address.update(street, city)
println("Updated address for $name")
}
}
class Address(var street: String, var city: String) {
fun update(street: String, city: String) {
this.street = street
this.city = city
}
}
→ Customer が責務を持つことで、Address が Customer に依存しなくなった。
After②:仲介役を導入
class AddressUpdater {
fun update(customer: Customer, street: String, city: String) {
customer.address.update(street, city)
println("Updated address for ${customer.name}")
}
}
class Customer(val name: String, val address: Address)
class Address(var street: String, var city: String) {
fun update(street: String, city: String) {
this.street = street
this.city = city
}
}
→ Customer と Address の直接的な親密さを Updater で調整。
25.4 実務での指針
- 双方向依存は極力避ける(依存方向を一方通行に)
- getter 連発は「親密さのサイン」 → メソッド移動で解消
- 境界を越えるアクセスが多い場合は Mediator / Facade / DTO を導入
- Clean Architecture / DDD を適用して 層の責務を整理
まとめ
- Inappropriate Intimacy は「2つのクラスがべったり結合」している悪臭
- 解決策は Move Method / Move Field / Mediator / Dependency Inversion
- 基本思想:クラス間の距離は適度に保つ。疎結合こそ健全な設計