1. 概要(Overview)
Move Method は代表的なリファクタリング手法の一つです。
あるメソッドが自分のクラスのフィールドをほとんど使わず、別のクラスのデータに強く依存している 場合、そのメソッドは「居場所を間違えている」可能性があります。
Move Method の目的は、そのメソッドを より適切なクラスへ移動させる ことで、データと振る舞いを同じ場所にまとめ、クラス間の結合度を下げることです。
2. 適用シーン(When to Use)
- メソッドが自分のクラスのフィールドをほとんど使わない
- 代わりに 他クラスのフィールドやメソッドを多用 している
- 呼び出しが不自然に見える
bankService.canWithdraw(account, 100.0)
本来は Account の責務なのに BankService が持っている、といったケースです。
よくある匂い:
- Feature Envy(機能嫉妬)
- Inappropriate Intimacy(不適切な親密さ)
3. 手順(Mechanics / Steps)
- 移動先クラスを決定(最も強く依存しているクラス)
- 移動先クラスに新しいメソッドを作成し、処理をコピー
- 呼び出し元を新しいメソッドに置き換える
- 互換性が必要なら元のクラスに 委譲メソッド(delegating method) を残す
- 最終的に元メソッドを削除
4. Kotlin 例(Before → After)
Before:他人のデータに依存
data class Account(val balance: Double, val overdraftLimit: Double)
class BankService {
fun canWithdraw(account: Account, amount: Double): Boolean {
return account.balance - amount >= -account.overdraftLimit
}
}
-
canWithdrawはBankServiceにあるが、使っているのはAccountのデータだけ - このロジックは Account 本人の責務
After①:Account に移動
data class Account(val balance: Double, val overdraftLimit: Double) {
fun canWithdraw(amount: Double): Boolean {
return balance - amount >= -overdraftLimit
}
}
→ Account 自身が「引き出せるかどうか」を判断。
→ データと振る舞いが一体化 し、内聚性が高まった。
After②:委譲メソッドを残す場合
class BankService {
fun canWithdraw(account: Account, amount: Double): Boolean =
account.canWithdraw(amount) // 委譲
}
→ 既存の呼び出しを壊さずに段階的に移行可能。
5. 効果(Benefits)
- データとロジックが同じクラスに集まる → カプセル化強化
- クラス間の結合度が下がる → 疎結合化
- 意図が明確になり、コードの可読性が向上
- テスト対象が明確になり、単体テストが容易に
6. 注意点(Pitfalls)
- 移動しすぎると God Class 化のリスク
- 複数クラスにまたがるロジックなら、移動ではなく 新しいクラスを導入 すべき場合もある
- 外部 API を公開している場合は、委譲メソッド を残して互換性を保つと安全
まとめ
- Move Method は「メソッドを正しい居場所に移す」リファクタリング
- 判断基準:そのメソッドはどのクラスのデータを一番使っているか?
- 基本思想:データと振る舞いを一緒に置くことで、カプセル化と内聚性を守る