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?

【リファクタリング】第12回 Switch 文(Switch Statements)

Posted at

はじめに

switchwhen を使った 分岐の羅列 は、典型的なコードの悪臭のひとつです。
特に同じような switch/when複数箇所に重複 して現れる場合、保守性と拡張性を大きく損ないます。

代表的な解決策は 多態(Polymorphism)への置き換え です。


12.1 特徴

  • switch / when が長大化し、条件分岐の塊 になっている
  • 新しい条件を追加するたびにすべての switch を修正 しなければならない
  • 同じような switch が複数の場所で繰り返される
  • OCP(開放/閉鎖の原則)に違反している

12.2 解決手法

  • 多態への置換(Replace Conditional with Polymorphism)
    → 各条件をクラスやオブジェクトに分ける
  • 戦略パターン(Strategy Pattern)
    → アルゴリズムの差し替えをオブジェクトに委譲
  • 状態パターン(State Pattern)
    → オブジェクトの状態遷移をクラスにカプセル化
  • sealed class + when(Kotlin の強力なパターンマッチ)
    → 分岐が必要な場合でもコンパイラによる網羅性チェックを活用

12.3 Kotlin 例

Before:冗長な when 分岐

fun getPay(employee: Employee): Double {
    return when (employee.type) {
        "engineer" -> employee.baseSalary
        "manager" -> employee.baseSalary + employee.bonus
        "sales" -> employee.baseSalary + employee.commission
        else -> throw IllegalArgumentException("Unknown type")
    }
}
  • 新しい職種を追加するたびに when を修正
  • 他の場所にも同じ分岐があれば、修正漏れのリスク大

After①:ポリモーフィズムで置換

sealed class Employee(open val baseSalary: Double) {
    abstract fun getPay(): Double
}

data class Engineer(override val baseSalary: Double) : Employee(baseSalary) {
    override fun getPay() = baseSalary
}

data class Manager(override val baseSalary: Double, val bonus: Double) : Employee(baseSalary) {
    override fun getPay() = baseSalary + bonus
}

data class Sales(override val baseSalary: Double, val commission: Double) : Employee(baseSalary) {
    override fun getPay() = baseSalary + commission
}

呼び出し側:

val employees: List<Employee> = listOf(
    Engineer(3000.0),
    Manager(4000.0, bonus = 1000.0),
    Sales(2500.0, commission = 800.0)
)

for (e in employees) {
    println("Pay: ${e.getPay()}")
}

when 文が消え、OCP を満たす設計に。


After②:Strategy パターン

interface PayPolicy {
    fun calculate(base: Double): Double
}

class EngineerPolicy : PayPolicy {
    override fun calculate(base: Double) = base
}

class ManagerPolicy(private val bonus: Double) : PayPolicy {
    override fun calculate(base: Double) = base + bonus
}

class SalesPolicy(private val commission: Double) : PayPolicy {
    override fun calculate(base: Double) = base + commission
}

data class Employee(val baseSalary: Double, val payPolicy: PayPolicy) {
    fun getPay() = payPolicy.calculate(baseSalary)
}

→ ロジックを 戦略オブジェクトに委譲
→ 動的に切り替え可能。


12.4 実務での指針

  • switch/when2 箇所以上に重複 → リファクタリングを検討
  • 新しい条件追加で 既存コードを修正しなければならない → ポリモーフィズムに移行
  • 状態遷移は State パターン、アルゴリズム切替は Strategy パターン
  • Kotlin の sealed class + when は「網羅性保証」があるため、限定的に有効

まとめ

  • Switch 文の氾濫は「保守コストの爆弾」
  • 解決策は ポリモーフィズム・Strategy・State パターン
  • 基本思想「分岐ではなく、振る舞いをオブジェクトに委譲する」

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?