状态机设计模式
好的 👍,我们来系统且详细地介绍状态机设计模式(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 集中式实现。
- 适用场景:流程控制、播放器、订单、工作流等。
- 优点:简化逻辑、可扩展性强、职责清晰。