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?

【リファクタリング】第13回 一時フィールド(Temporary Field)

Posted at

はじめに

一時フィールド(Temporary Field) とは、クラスに定義されているフィールドが
「特定の状況でしか使われず、普段は無効・null のまま」になっている状態を指します。

これはクラスが 本来の責務以上を抱えているサイン であり、読み手を混乱させる悪臭です。


13.1 特徴

  • フィールドが常に使われるわけではなく、一部のメソッドでしか利用されない
  • null チェックや isInitialized チェックがあちこちに散乱する
  • コンストラクタで無理に初期化されているが、実際は不要なケースが多い
  • クラスが「複数の文脈」を抱え込みすぎている

13.2 解決手法

  • クラス抽出(Extract Class)
    → 一時フィールドとそれを使うメソッドを別クラスに移動
  • メソッドオブジェクト化(Introduce Method Object)
    → 複雑な処理専用のクラスを作り、一時フィールドをそこで完結させる
  • ローカル変数への置換
    → メソッド内で完結できる場合はフィールドにせずローカルに閉じ込める

13.3 Kotlin 例

Before:一時フィールドがあるクラス

class ReportGenerator {
    private var tempData: String? = null  // 普段は null
    private var tempFormat: String? = null

    fun generateSummary(data: String): String {
        this.tempData = data
        this.tempFormat = "SUMMARY"
        return process()
    }

    fun generateDetail(data: String): String {
        this.tempData = data
        this.tempFormat = "DETAIL"
        return process()
    }

    private fun process(): String {
        return when (tempFormat) {
            "SUMMARY" -> "Summary of $tempData"
            "DETAIL" -> "Detail of $tempData"
            else -> ""
        }
    }
}
  • tempDatatempFormat一時的にしか使われないフィールド
  • クラスに残り続け、読み手に混乱を与える

After①:メソッドオブジェクト化

class ReportGenerator {
    fun generateSummary(data: String) =
        ReportTask(data, "SUMMARY").process()

    fun generateDetail(data: String) =
        ReportTask(data, "DETAIL").process()
}

private class ReportTask(
    val data: String,
    val format: String
) {
    fun process(): String =
        when (format) {
            "SUMMARY" -> "Summary of $data"
            "DETAIL" -> "Detail of $data"
            else -> ""
        }
}

→ 一時フィールドを ReportTask に閉じ込め、ReportGenerator はシンプルに。


After②:ローカル変数化(単純ケース)

class ReportGenerator {
    fun generateSummary(data: String): String {
        val format = "SUMMARY"
        return process(data, format)
    }

    fun generateDetail(data: String): String {
        val format = "DETAIL"
        return process(data, format)
    }

    private fun process(data: String, format: String): String =
        when (format) {
            "SUMMARY" -> "Summary of $data"
            "DETAIL" -> "Detail of $data"
            else -> ""
        }
}

→ フィールド不要、ローカル変数で十分。


13.4 実務での指針

  • 一時的にしか使わないフィールドは匂い
  • 「そのフィールドはこのクラスに本当に属しているか?」を問い直す
  • null チェックが多発 → クラス分割を検討
  • 複雑処理のためだけに存在するなら メソッドオブジェクト化 を適用

まとめ

  • Temporary Field は「クラスが余計な責務を抱えているサイン」
  • 解決策は Extract Class / Introduce 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?