👨🏻‍💻 编程

状态机设计模式

好的 👍,我们来系统且详细地介绍状态机设计模式(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 中,维护困难。
  • 新增状态需要修改多个地方。
  • 容易遗漏状态分支。

六、总结

  1. 状态模式定义:通过将状态封装为类,使对象在不同状态下行为不同,避免复杂条件分支。
  2. 核心角色:Context(环境类)、State(抽象状态)、ConcreteState(具体状态)。
  3. Kotlin 实现方式
    • 传统接口 + 多个状态类。
    • sealed class 集中式实现。
  4. 适用场景:流程控制、播放器、订单、工作流等。
  5. 优点:简化逻辑、可扩展性强、职责清晰。

留言

您的邮箱地址不会被公开。 必填项已用 * 标注