はじめに
switch や when を使った 分岐の羅列 は、典型的なコードの悪臭のひとつです。
特に同じような 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/whenが 2 箇所以上に重複 → リファクタリングを検討 - 新しい条件追加で 既存コードを修正しなければならない → ポリモーフィズムに移行
- 状態遷移は State パターン、アルゴリズム切替は Strategy パターン
- Kotlin の sealed class + when は「網羅性保証」があるため、限定的に有効
まとめ
- Switch 文の氾濫は「保守コストの爆弾」
- 解決策は ポリモーフィズム・Strategy・State パターン
- 基本思想:「分岐ではなく、振る舞いをオブジェクトに委譲する」