状态机设计模式
好的 👍,我们来系统且详细地介绍状态机设计模式(State Pattern,状态模式),并结合 Kotlin 示例讲解。
一、状态机设计模式的核心思想
1. 定义
**状态模式(State Pattern)**是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为。
换句话说:状态模式将对象的行为封装到不同的状态类中,使得对象在不同状态下表现出不同的行为,而不是通过冗长的 if-else 或 when 来硬编码状态切换。
2. 适用场景
- 对象有多种状态,并且在不同状态下表现不同行为。
- 状态转换逻辑复杂,代码中出现了大量 if-else 或 switch/when 分支。
- 希望 状态切换和行为解耦,避免在主逻辑类中堆积过多状态逻辑。
- 希望新增状态时不修改原有代码,只需扩展新的状态类(符合开闭原则)。
常见应用:
- 播放器(播放、暂停、停止)
- 订单流程(待支付、已支付、发货中、完成)
- 工作流引擎(草稿、审核中、已发布)
- 网络连接(连接中、已连接、断开)
3. 优点
- 清晰可维护:避免状态相关的大量条件分支。
- 扩展性好:新增状态只需增加一个类。
- 职责单一:每个状态类只关心自己的行为和状态转换。
4. 缺点
- 类数量增加:每个状态对应一个类,状态过多时会膨胀。
- 状态类间耦合:某些状态需要了解其他状态类的存在。
二、状态模式的结构
UML 类图
┌───────────────┐ │ Context │ │ (环境类) │ │ - state:State │ │ + request() │ └───────▲───────┘ │ ┌───────┴────────┐ │ │ ┌─────────────┐ ┌─────────────┐ │ State │ │ ConcreteState│ │ (抽象状态) │ │ (具体状态) │ │ + handle() │ │ + handle() │ └─────────────┘ └─────────────┘
- Context(环境类):持有当前状态对象,外部调用入口。
- State(抽象状态):定义状态接口。
- ConcreteState(具体状态):实现不同状态下的行为逻辑。
三、Kotlin 示例
我们以 音乐播放器(播放、暂停、停止) 为例。
1. 定义状态接口
// 状态接口 interface PlayerState { fun play(context: MusicPlayer) fun pause(context: MusicPlayer) fun stop(context: MusicPlayer) }
2. 具体状态类
// 播放状态 class PlayingState : PlayerState { override fun play(context: MusicPlayer) { println("已经在播放,无需重复播放") } override fun pause(context: MusicPlayer) { println("暂停播放") context.changeState(PausedState()) } override fun stop(context: MusicPlayer) { println("停止播放") context.changeState(StoppedState()) } } // 暂停状态 class PausedState : PlayerState { override fun play(context: MusicPlayer) { println("继续播放") context.changeState(PlayingState()) } override fun pause(context: MusicPlayer) { println("已经暂停") } override fun stop(context: MusicPlayer) { println("从暂停状态停止播放") context.changeState(StoppedState()) } } // 停止状态 class StoppedState : PlayerState { override fun play(context: MusicPlayer) { println("开始播放") context.changeState(PlayingState()) } override fun pause(context: MusicPlayer) { println("无法暂停,当前已停止") } override fun stop(context: MusicPlayer) { println("已经停止") } }
3. 环境类(Context)
class MusicPlayer { private var state: PlayerState = StoppedState() // 初始为停止状态 fun changeState(newState: PlayerState) { state = newState } fun play() = state.play(this) fun pause() = state.pause(this) fun stop() = state.stop(this) }
4. 使用示例
fun main() { val player = MusicPlayer() player.play() // 开始播放 player.pause() // 暂停播放 player.play() // 继续播放 player.stop() // 停止播放 player.pause() // 无法暂停,当前已停止 }
四、Kotlin 中的改进写法
由于 Kotlin 支持 sealed class / sealed interface,我们可以让状态定义更简洁,避免过多类文件。
使用
sealed class
重写
sealed class PlayerState { abstract fun play(context: MusicPlayer) abstract fun pause(context: MusicPlayer) abstract fun stop(context: MusicPlayer) object Playing : PlayerState() { override fun play(context: MusicPlayer) = println("已经在播放") override fun pause(context: MusicPlayer) { println("暂停播放") context.changeState(Paused) } override fun stop(context: MusicPlayer) { println("停止播放") context.changeState(Stopped) } } object Paused : PlayerState() { override fun play(context: MusicPlayer) { println("继续播放") context.changeState(Playing) } override fun pause(context: MusicPlayer) = println("已经暂停") override fun stop(context: MusicPlayer) { println("从暂停状态停止播放") context.changeState(Stopped) } } object Stopped : PlayerState() { override fun play(context: MusicPlayer) { println("开始播放") context.changeState(Playing) } override fun pause(context: MusicPlayer) = println("无法暂停,当前已停止") override fun stop(context: MusicPlayer) = println("已经停止") } } class MusicPlayer { private var state: PlayerState = PlayerState.Stopped fun changeState(newState: PlayerState) { state = newState } fun play() = state.play(this) fun pause() = state.pause(this) fun stop() = state.stop(this) }
这种写法优点:
- 所有状态集中管理,避免类文件过多。
- 编译器检查:sealed class 让状态扩展受限,避免遗漏。
五、对比 if-else 的优势
如果用传统写法:
class MusicPlayer { private var state = "stopped" fun play() { when (state) { "playing" -> println("已经在播放") "paused" -> { println("继续播放") state = "playing" } "stopped" -> { println("开始播放") state = "playing" } } } // 省略 pause, stop... }
缺点:
- 状态逻辑分散在 when/if 中,维护困难。
- 新增状态需要修改多个地方。
- 容易遗漏状态分支。
六、总结
- 状态模式定义:通过将状态封装为类,使对象在不同状态下行为不同,避免复杂条件分支。
- 核心角色:Context(环境类)、State(抽象状态)、ConcreteState(具体状态)。
- Kotlin 实现方式:
- 传统接口 + 多个状态类。
- sealed class 集中式实现。
- 适用场景:流程控制、播放器、订单、工作流等。
- 优点:简化逻辑、可扩展性强、职责清晰。