1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【リファクタリング】Replace Method with Method Object(メソッドをメソッドオブジェクトに置き換え)

Posted at

1. 概要(Overview)

Replace Method with Method Object は、長大なメソッド を専用のクラスに切り出し、その中で処理を実行するリファクタリングです。

メソッドが長く複雑になると、

  • ローカル変数が増えて処理が見通しづらい
  • 途中で Extract Method(メソッドの抽出) がしづらい
  • 修正が入りやすく「散弾銃のような変更(Shotgun Surgery)」になりがち

といった問題が起こります。

このリファクタリングでは、**メソッドをクラス化(Method Object 化)**し、フィールドとしてローカル変数を保持できるようにすることで、メソッドの分割や整理を容易にします。


2. 適用シーン(When to Use)

  • メソッドが長すぎる/複雑すぎる
  • ローカル変数が多く、Extract Method で簡単に切り出せない
  • 同じロジックを別の状況でも再利用したい
  • 状態を保持しながら複数の小メソッドに分割したい

よくある匂い:

  • Long Method(長すぎるメソッド)
  • Temporary Field(不要な一時変数)
  • Shotgun Surgery(散弾銃のような変更)

3. 手順(Mechanics / Steps)

  1. 問題のある長いメソッドを特定
  2. 新しいクラスを作成し、そのクラスに元メソッドのローカル変数をフィールドとして移動
  3. 元メソッドを新しいクラスのインスタンス生成+実行呼び出しに置き換える
  4. 新しいクラスの中でメソッドを小さく分割(Extract Method を適用)
  5. テストを実行して動作確認

4. Kotlin 例(Before → After)

Before:長大なメソッド

class Order(val items: List<Double>) {
    fun calculateSummary(discountRate: Double): Double {
        var subtotal = items.sum()
        val discount = subtotal * discountRate
        subtotal -= discount
        val tax = subtotal * 0.1
        return subtotal + tax
    }
}
  • ロジックがまとまっていて一見シンプルに見えるが、
    実際の業務ロジックでは 条件分岐や計算式が膨張しやすい。
  • subtotaldiscount といったローカル変数が増えると、Extract Method が難しくなる。

After①:メソッドをクラス化

class Order(val items: List<Double>) {
    fun calculateSummary(discountRate: Double): Double {
        return OrderSummaryCalculator(this, discountRate).calculate()
    }
}

class OrderSummaryCalculator(
    private val order: Order,
    private val discountRate: Double
) {
    private var subtotal: Double = 0.0

    fun calculate(): Double {
        subtotal = order.items.sum()
        applyDiscount()
        val tax = subtotal * 0.1
        return subtotal + tax
    }

    private fun applyDiscount() {
        val discount = subtotal * discountRate
        subtotal -= discount
    }
}

→ ロジックをクラスに移し、フィールドとして保持できるため 責務の分割 が容易に。


After②:さらに小さいメソッドに分割

class OrderSummaryCalculator(
    private val order: Order,
    private val discountRate: Double
) {
    private var subtotal: Double = 0.0

    fun calculate(): Double {
        calculateSubtotal()
        applyDiscount()
        return addTax()
    }

    private fun calculateSubtotal() {
        subtotal = order.items.sum()
    }

    private fun applyDiscount() {
        subtotal -= subtotal * discountRate
    }

    private fun addTax(): Double {
        return subtotal + subtotal * 0.1
    }
}

→ 小さなメソッドに分割でき、テストしやすく保守性が高い設計に。


5. 効果(Benefits)

  • 長大なメソッドを整理可能
  • ローカル変数をフィールド化でき、Extract Method が適用しやすい
  • ロジックを分割・テストしやすくなる
  • 状態を持った「計算オブジェクト」として再利用可能

6. 注意点(Pitfalls)

  • 小規模なメソッドに対して使うと逆に冗長になる
  • クラスを増やすため、設計全体が複雑になりすぎないよう注意
  • 「このメソッドは本当にクラス化するほどの責務を持っているか?」を判断する必要がある

まとめ

  • Replace Method with Method Object は「長大なメソッドをクラスに切り出して整理」するリファクタリング
  • 判断基準:ローカル変数が多すぎてメソッド抽出が難しいか?
  • 基本思想:状態を保持できる専用オブジェクトに変換し、責務を分割して保守性を高める

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?