0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【リファクタリング】Pull Up Method(メソッドの引き上げ)

Posted at

1. 概要(Overview)

Pull Up Method は、サブクラスに重複して存在する 同一/ほぼ同一のメソッド実装
スーパークラスへ移動(引き上げ) して共通化するリファクタリングです。

目的

  • 重複実装を一箇所に集約して保守性を上げる
  • 振る舞いの“共通契約”を上位で明示し、設計をクリアにする
  • バグ修正や仕様変更の波及を最小化

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

  • 複数のサブクラスに 同じロジック のメソッドがある
  • メソッド名は同じだが実装がごく小さな差分しかない(共通化可能)
  • 上位型(ポリモーフィックに扱う)経由で共通メソッドを呼びたい

よくある匂い:

  • Duplicate Code(重複コード)
  • Parallel Inheritance Hierarchies(並行継承階層)

3. 手順(Mechanics / Steps)

  1. サブクラスの重複メソッドを比較し、共通部分を特定
  2. 共通化できるなら、その実装を スーパークラスへ移動
  3. 差分が残る場合は:
    • Template Method を導入(フックメソッドで差分を下位へ)
    • もしくは差分を 別メソッドに抽出 してオーバーライドさせる
  4. サブクラス側の重複定義を削除
  5. テストを実行し回帰を確認

4. Kotlin 例(Before → After)

4.1 完全一致するメソッドの引き上げ

Before

open class Employee

class Engineer(val name: String) : Employee() {
    fun displayInfo(): String = "Employee: $name"
}

class Manager(val name: String) : Employee() {
    fun displayInfo(): String = "Employee: $name"
}

After

open class Employee(open val name: String) {
    fun displayInfo(): String = "Employee: $name"
}

class Engineer(override val name: String) : Employee(name)

class Manager(override val name: String) : Employee(name)
  • 重複していた displayInfo()Employee に引き上げ

4.2 小さな差分がある場合:Template Method で引き上げ

Before

class CsvReport : Report() {
    fun export(): String = "header\n" + buildBody() + "\nfooter"
    private fun buildBody() = "csv rows"
}

class JsonReport : Report() {
    fun export(): String = "{ \"header\": true, " + buildBody() + ", \"footer\": true }"
    private fun buildBody() = "\"json\": \"rows\""
}

open class Report

After(共通フローを上位に、差分はフックで)

open class Report {
    fun export(): String = wrap(buildBody())
    protected open fun buildBody(): String = ""
    protected open fun wrap(body: String): String = body
}

class CsvReport : Report() {
    override fun buildBody(): String = "csv rows"
    override fun wrap(body: String): String = "header\n$body\nfooter"
}

class JsonReport : Report() {
    override fun buildBody(): String = "\"json\": \"rows\""
    override fun wrap(body: String): String =
        "{ \"header\": true, $body, \"footer\": true }"
}
  • 共通の手順(export フロー)を上位へ引き上げ、差分はオーバーライド

5. 効果(Benefits)

  • 重複排除により修正コストを削減
  • 共通 API を スーパークラスで保証(契約の明確化)
  • ポリモーフィズムで上位型経由の呼び出しが可能に

6. 注意点(Pitfalls)

  • 「本当にすべてのサブクラスで同じか?」を慎重に評価
    • 一部のみなら Pull Up の対象外、または 抽象メソッド化を検討
  • 無理な引き上げは スーパークラスの責務過多(God Class 化) を招く
  • 共有化で隠れた依存が増えるなら、**委譲(委譲オブジェクト)**や Strategy も検討

まとめ

  • Pull Up Method は、サブクラスに散らばる同一/類似メソッドを 上位へ集約して保守性を高める手法
  • 判断基準:共通部分が十分に大きいか? 上位に置くのが自然か?
  • 差分が残るなら Template Method / Strategy / Hook メソッド を併用して設計を整える

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?