0赞
赏
赞赏
更多好文
从“面条代码”到“清晰架构”,让分支逻辑呼吸自由
🌪️ 你是否也经历过这些“when 痛点”?
// 典型臃肿 when:支付方式处理(200+ 行)
fun processPayment(type: String, amount: Double): String {
return when (type) {
"ALIPAY" -> {
// 30 行:验签、调用 SDK、记录日志、异常处理...
}
"WECHAT" -> {
// 28 行:生成预支付订单、回调处理...
}
"UNIONPAY" -> { ... } // 15+ 分支,每个含复杂逻辑
"CREDIT_CARD" -> { ... }
// ... 新增分支需修改此函数,违反开闭原则
else -> "UNSUPPORTED"
}
}
❌ 症状诊断:
- 🔴 单函数超 150 行,Git Diff 惨不忍睹
- 🔴 新增支付方式需修改核心函数(违反开闭原则)
- 🔴 单元测试需覆盖所有分支,测试代码膨胀
- 🔴 字符串硬编码,拼写错误编译不报错
- 🔴 逻辑耦合:业务规则、网络调用、日志混杂
💡 核心原则:
“when 表达式应是调度器,而非逻辑仓库”
本文提供 5 种经生产验证的重构策略,附代码对比+选型指南
🛠️ 重构策略全景图(附适用场景)
| 策略 | 适用场景 | 优势 | 注意事项 |
|---|---|---|---|
| 密封类 + 多态 | 分支类型固定、逻辑差异大 | 编译时安全、天然开闭原则 | 需 Kotlin 1.5+ |
| 策略映射表 | 字符串/枚举分支、动态注册 | 零反射、性能优、易扩展 | 需处理未注册 key |
| 扩展函数分散 | 枚举分支、轻量逻辑 | 代码分散、调用直观 | 不适合复杂逻辑 |
| 状态模式 | 状态机流转(订单/审批流) | 清晰表达状态迁移 | 增加类数量 |
| 提取纯函数 | 分支内逻辑复杂但分支少 | 降低认知负荷 | 未解决分支膨胀 |
🔑 策略一:密封类 + 多态(类型安全的终极方案)
🌰 重构前(字符串 when)
fun calculateDiscount(type: String, price: Double): Double =
when (type) {
"VIP" -> price * 0.8
"MEMBER" -> price * 0.9
"NEW_USER" -> price * 0.95
else -> price
}
✅ 重构后(密封类 + 多态)
// 1. 定义密封类体系(编译器保障完整性)
sealed interface UserDiscount {
fun apply(price: Double): Double
object Vip : UserDiscount {
override fun apply(price: Double) = price * 0.8
}
object Member : UserDiscount {
override fun apply(price: Double) = price * 0.9
}
object NewUser : UserDiscount {
override fun apply(price: Double) = price * 0.95
}
object None : UserDiscount {
override fun apply(price: Double) = price
}
}
// 2. 调用处:简洁且类型安全
fun calculateDiscount(discount: UserDiscount, price: Double) =
discount.apply(price)
// 3. 使用示例
val finalPrice = calculateDiscount(UserDiscount.Vip, 100.0)
✨ 优势:
- ✅ 新增折扣类型只需新增 object,无需修改调用方(开闭原则)
- ✅ 编译器检查分支完整性,杜绝
else遗漏 - ✅ 每个类型逻辑独立,单元测试聚焦单一职责
- ✅ 与 Compose 完美配合:
@Preview可单独预览每种状态
🔑 策略二:策略映射表(动态注册的轻量方案)
🌰 重构支付处理(告别长 when)
// 1. 定义策略接口
interface PaymentStrategy {
fun process(amount: Double): PaymentResult
fun supports(type: PaymentType): Boolean
}
// 2. 策略实现(分散在独立文件)
object AlipayStrategy : PaymentStrategy {
override fun process(amount: Double) = /* ... */
override fun supports(type: PaymentType) = type == PaymentType.ALIPAY
}
object WechatStrategy : PaymentStrategy { /* ... */ }
// 3. 策略注册中心(支持动态扩展!)
object PaymentProcessor {
private val strategies = mutableListOf<PaymentStrategy>()
fun register(strategy: PaymentStrategy) {
strategies.add(strategy)
}
fun process(type: PaymentType, amount: Double): PaymentResult {
val strategy = strategies.firstOrNull { it.supports(type) }
?: throw UnsupportedOperationException("Unsupported payment: $type")
return strategy.process(amount)
}
// 初始化注册(Application.onCreate)
fun init() {
register(AlipayStrategy)
register(WechatStrategy)
// 新增支付方式?只需 register(NewStrategy)!
}
}
// 4. 调用处:一行搞定
val result = PaymentProcessor.process(PaymentType.WECHAT, 99.9)
✨ 优势:
- ✅ 新增支付方式零修改:只需实现策略 + 注册
- ✅ 支持运行时动态注册(插件化场景友好)
- ✅ 策略可独立测试,无分支覆盖压力
- ✅ 性能优于反射(Map 查找 O(1))
🔑 策略三:扩展函数分散(枚举场景神器)
enum class NotificationType { EMAIL, SMS, PUSH, IN_APP }
// 为每种类型定义专属扩展函数
fun NotificationType.send(user: User, content: String) {
when (this) {
NotificationType.EMAIL -> EmailService.send(user.email, content)
NotificationType.SMS -> SmsService.send(user.phone, content)
NotificationType.PUSH -> PushService.send(user.deviceId, content)
NotificationType.IN_APP -> InAppService.show(user.id, content)
}
}
// 调用处:语义清晰
NotificationType.EMAIL.send(user, "订单已发货")
💡 进阶技巧:结合 sealed class + extension function
sealed class Notification {
object Email : Notification()
object Sms : Notification()
fun send(user: User, content: String) = when (this) {
is Email -> EmailService.send(...)
is Sms -> SmsService.send(...)
}
}
// 调用:Notification.Email.send(user, content)
🔑 策略四:状态模式(复杂状态机救星)
// 订单状态机重构
sealed class OrderState {
object Pending : OrderState()
object Paid : OrderState()
object Shipped : OrderState()
object Completed : OrderState()
fun nextState(action: OrderAction): OrderState = when (this) {
is Pending -> if (action == OrderAction.PAY) Paid else this
is Paid -> if (action == OrderAction.SHIP) Shipped else this
// ... 清晰表达状态迁移规则
else -> this
}
}
// 业务层:专注状态流转
fun updateOrderState(current: OrderState, action: OrderAction): OrderState {
return current.nextState(action)
}
✅ 比长 when 优势:
- 状态迁移规则集中定义,避免散落在各处
- 新增状态只需扩展密封类,迁移逻辑自动生效
- 可视化状态图(配合文档工具)
🌟 终极心法:何时保留 when?何时重构?
| 场景 | 建议 | 理由 |
|---|---|---|
| 分支 ≤ 3 且逻辑简单 | ✅ 保留 when | 过度设计反而降低可读性 |
| 分支基于枚举/密封类 | ✅ 用 when(但提取函数) | 编译器保障安全,简洁直观 |
| 分支含复杂业务逻辑 | ❌ 重构! | 违反单一职责原则 |
| 需频繁新增分支 | ❌ 重构! | 违反开闭原则 |
| 字符串/数字硬编码分支 | ❌ 重构! | 类型不安全,易出错 |
📌 轻量优化技巧(当必须用 when 时)
// 1. 提取函数:让 when 只做调度
fun processPayment(type: PaymentType, amount: Double) =
when (type) {
PaymentType.ALIPAY -> processAlipay(amount)
PaymentType.WECHAT -> processWechat(amount)
// ...
}
private fun processAlipay(amount: Double) { /* 专注支付宝逻辑 */ }
// 2. 使用 exhaustive when(密封类)
val result = when (state) {
is Loading -> handleLoading()
is Success -> handleSuccess()
is Error -> handleError()
// 编译器强制覆盖所有分支!
}
📋 重构 Checklist(行动指南)
- 识别信号:when 表达式超过 15 行?分支含 >10 行逻辑?
- 优先密封类:分支类型固定 → 用 sealed interface/class
- 动态场景选映射:需运行时扩展 → 策略注册中心
- 枚举分支用扩展:为枚举定义专属行为函数
- 状态机用状态模式:清晰表达迁移规则
- 保留简单 when:3 分支内 + 逻辑简单 → 无需重构
- 单元测试验证:重构后每个策略/状态独立测试
💫 结语:优雅代码是设计出来的,不是堆出来的
臃肿的 when 表达式本质是职责混淆的警报:
它在呐喊:“请把我拆解!我的每个分支都值得拥有独立的生命!”
真正的 Kotlin 之道:
🔹 用类型系统代替字符串魔法
🔹 用多态代替条件分支
🔹 用组合代替嵌套逻辑
🌱 今日行动:
1️⃣ 打开你的代码库,搜索when (
2️⃣ 找出最长的那个,用本文策略重构 1 个分支
3️⃣ 感受代码“呼吸”变顺畅的瞬间
告别面条代码,从重构第一个 when 开始。
你写的每一行清晰代码,都是对未来的自己最温柔的馈赠。✨
📚 延伸资源
- Kotlin 官方:密封类最佳实践
- 《重构:改善既有代码的设计》第 10 章:简化条件逻辑
- 开源参考:Square Workflow(状态机设计范例)
- 工具推荐:IntelliJ IDEA →
Refactor → Replace Conditional with Polymorphism
