1. 概要(Overview)
Move Field は代表的なリファクタリング手法の一つです。
あるフィールドが自分のクラスではほとんど使われず、別のクラスのメソッドやロジックに強く依存している 場合、そのフィールドは「居場所を間違えている」可能性があります。
Move Field の目的は、そのフィールドを より適切なクラスへ移動させる ことで、データと振る舞いを同じ場所にまとめ、クラス間の依存を減らすことです。
2. 適用シーン(When to Use)
- フィールドが所属クラスではほとんど使われない
- 代わりに 他クラスのメソッドから頻繁に参照されている
- フィールドの存在がクラス間の不自然な依存を生んでいる
よくある匂い:
- Feature Envy(機能嫉妬)
- Inappropriate Intimacy(不適切な親密さ)
3. 手順(Mechanics / Steps)
- フィールドの利用状況を分析し、移動先を決定(最も利用されているクラス)
- 移動先クラスにフィールドを新規追加
- フィールドを使うメソッドを移動先クラスへ修正
- 互換性が必要なら元クラスに アクセサや委譲メソッド を残す
- 全ての呼び出しが修正できたら元のフィールドを削除
4. Kotlin 例(Before → After)
Before:フィールドが他クラスに依存
class Account(
val customer: Customer,
val balance: Double
)
class Customer(
val name: String
) {
// 本当はこの顧客に関連する情報なのに…
}
class BankService {
fun printCustomerInfo(account: Account) {
println("${account.customer.name} - Discount: ${account.discountRate}")
}
}
// ❌ discountRate が Account に置かれているが、Customer の属性に近い
var Account.discountRate: Double
get() = 0.1
set(value) {}
-
discountRateはAccountにあるが、実際にはCustomerに属する方が自然。
After①:Customer に移動
class Account(
val customer: Customer,
val balance: Double
)
class Customer(
val name: String,
var discountRate: Double // ✅ 移動した
)
class BankService {
fun printCustomerInfo(account: Account) {
println("${account.customer.name} - Discount: ${account.customer.discountRate}")
}
}
→ フィールドが 正しいクラス(Customer) に移動し、凝集度が高まった。
After②:互換性のため委譲を残す
val Account.discountRate: Double
get() = customer.discountRate
→ 既存コードを壊さずに段階的移行が可能。
5. 効果(Benefits)
- データが「本来あるべき場所」にまとまる → カプセル化強化
- クラスの責務が整理される → 高凝集
- 不自然な依存関係が減る → 疎結合化
- クラス設計がシンプルになり、保守性向上
6. 注意点(Pitfalls)
- フィールドの利用状況が複雑だと、移動によって依存が逆に増える場合がある
- 複数クラスにまたがる性質を持つ場合は、新しいクラスを導入する ほうが適切なこともある
- 既存の API を公開している場合は、委譲プロパティ を残して安全に移行するのが望ましい
まとめ
- Move Field は「フィールドを正しい居場所に移す」リファクタリング
- 判断基準:そのフィールドはどのクラスから一番使われているか?
- 基本思想:データを適切に配置し、クラスの責務を整理することで、可読性・保守性を高める