1. 概要(Overview)
Introduce Foreign Method は、自分で修正できないクラス(外部ライブラリや標準クラスなど)に新しいメソッドを追加したいときに使うリファクタリングです。
直接そのクラスを変更することはできないので、ユーティリティメソッドや**拡張メソッド(Kotlin の場合)**として外部に定義します。
目的は以下の通りです:
- 外部クラスに対する便利な操作を追加する
- 繰り返し登場する処理を共通化する
- クライアントコードをシンプルに保つ
2. 適用シーン(When to Use)
- 外部クラスに対して「こういう便利メソッドがあればいいのに」と思う
- その処理がプロジェクト内で繰り返し使われている
- しかし外部ライブラリや標準クラスを直接編集できない
例:
-
Dateクラスに「翌日を取得する」メソッドを追加したい -
Stringクラスに「メールアドレスかどうか判定する」メソッドを追加したい
3. 手順(Mechanics / Steps)
- 外部クラスに必要な処理を特定
- その処理をユーティリティメソッドまたは拡張関数として定義
- クライアントコードで新メソッドを利用するように修正
- テストで正しく動作することを確認
4. Kotlin 例(Before → After)
Before:毎回同じ処理を書いている
import java.time.LocalDate
fun printTomorrow(date: LocalDate) {
val tomorrow = date.plusDays(1) // 毎回 plusDays(1) を書くのが面倒
println("Tomorrow: $tomorrow")
}
-
LocalDateに「翌日を取得する」メソッドがあればもっと自然に書ける
After①:拡張関数を導入
import java.time.LocalDate
// Foreign Method を拡張関数として追加
fun LocalDate.nextDay(): LocalDate = this.plusDays(1)
fun printTomorrow(date: LocalDate) {
println("Tomorrow: ${date.nextDay()}")
}
→ LocalDate に本当にあるかのように nextDay() が使える。
After②:ユーティリティクラスとして定義する場合
object DateUtils {
fun nextDay(date: LocalDate): LocalDate = date.plusDays(1)
}
fun printTomorrow(date: LocalDate) {
println("Tomorrow: ${DateUtils.nextDay(date)}")
}
→ 拡張関数を使えない環境なら、ユーティリティで実装。
5. 効果(Benefits)
- 外部クラスに「欲しかったメソッド」を追加できる
- クライアントコードがシンプルで読みやすくなる
- 処理が共通化され、重複が減る
6. 注意点(Pitfalls)
- 拡張関数やユーティリティが増えすぎると管理が難しくなる
- 外部クラスの本来の API と衝突する可能性がある(名前の競合)
- プロジェクト全体で共通ルールを決めておかないと、乱立してスパゲッティ化 する危険性あり
まとめ
- Introduce Foreign Method は「外部クラスにメソッドを追加したい時に、拡張関数やユーティリティで代替する」リファクタリング
- 判断基準:この処理はそのクラスの自然な責務か?
- 基本思想:外部クラスを直接修正できない場合でも、利便性を追加してコードをシンプルに