程式裡有個依「型別字串」分支的大
when,每加一種型別就要回來改它,而且同樣的when(type)在好幾個地方重複出現。要怎麼重構?
🎯 Scenario
fun fee(type: String, amount: Int): Int = when (type) {
"regular" -> amount
"member" -> (amount * 0.9).toInt()
"vip" -> (amount * 0.8).toInt()
// 每加一種會員等級,就要回來改這裡……而且:
else -> error("unknown type")
}
fun label(type: String): String = when (type) { // 同樣的 when 又出現一次
"regular" -> "一般"
"member" -> "會員"
"vip" -> "VIP"
else -> "未知"
}
壞味道:新增一型要改多處(違反開放封閉原則 OCP)、when(type) 散落重複、else -> error 把編譯期該抓的漏掉延到執行期。
🧠 Intuition
TIP
先別看下面。問自己:這些分支的「型別」其實是什麼?如果把每一種型別變成一個物件,這些重複的 when 會發生什麼事?你會用哪個 pattern?
⚖️ Reference Design
答案:用多型 / 策略消滅 switch
把「型別」提升成一個帶行為的物件,讓分支邏輯各自歸位:
enum class Tier(val label: String, val discount: Double) {
REGULAR("一般", 1.0),
MEMBER ("會員", 0.9),
VIP ("VIP", 0.8);
fun fee(amount: Int) = (amount * discount).toInt()
}
// 呼叫端不再有任何 when:
Tier.VIP.fee(1000) // 800
Tier.VIP.label // "VIP"- 行為 / 資料跟著型別走,新增一型只改一個地方(加一個 enum 常數),其餘程式不動 → 滿足 OCP。
- 若各型別行為較複雜或要在執行期注入,就升級成 Strategy:
interface PricingStrategy+ 多個實作。
選策略的地方也別變成新的 switch
- 從外部輸入(字串 / 設定)對應到策略物件時,用查表 / 註冊表而不是又一個
when:
val byKey = Tier.entries.associateBy { it.name.lowercase() }
val tier = byKey[input] ?: error("unknown tier")- 否則只是把
when從「行為處」搬到「建立處」,味道沒消掉,只是換地方(見 Strategy 頁的誤用一節)。
🔑 Takeaways
- 「依型別分支的重複
when」是把行為放錯地方——讓行為跟著型別走(多型 / enum / Strategy)。 - 判準:行為簡單且封閉 → enum 帶方法;行為複雜或要執行期注入 → Strategy 介面。
- 重構後別在「選策略」處又長出新的
switch,用查表 / 註冊表對應。
No notes yet — jot your takeaways or Q&A here.