1. 概述

GoF分类

状态模式属于行为型设计模式,在《设计模式:可复用面向对象软件的基础》中被归类为对象行为模式。

官方定义

​允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。​​

核心目的

状态模式的主要目的是让对象在不同的状态下表现出不同的行为,而无需在代码中使用大量的条件判断语句。

解决的问题

当对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,状态模式可以:

  • 消除庞大的条件分支语句

  • 使状态转换显式化

  • 提高代码的可维护性和扩展性

cpp典型应用价值

  1. 游戏开发中的角色状态管理(站立、行走、跳跃等)

  2. 网络协议实现中的连接状态管理

  3. 工作流系统中的流程状态控制

  4. UI系统中的界面状态切换

关键特征

  • 将状态封装为独立的对象

  • 将状态相关的行为局部化到各自的状态类中

  • 状态转换由状态对象或上下文控制

  • 符合开闭原则,易于添加新状态

2. 核心思想

设计哲学

状态模式基于两个核心原则:

  1. ​单一职责原则​:每个状态类只负责一个特定状态下的行为

  2. ​开闭原则​:新增状态不需要修改现有代码

面向对象原则应用

  • ​封装变化​:将状态相关的行为封装在各自的状态类中

  • ​多态​:通过状态接口实现运行时多态

  • ​松耦合​:上下文类不依赖具体状态类,只依赖抽象状态接口

cpp实现特殊考虑

  1. ​资源管理​:使用智能指针管理状态对象生命周期

  2. ​异常安全​:确保状态转换时的异常安全

  3. ​性能​:避免频繁创建销毁状态对象,可考虑对象池

  4. ​线程安全​:在多线程环境中保护状态转换

工作流程示意图

[Client] --> [Context] --> [State]
                  |            |
                  v            v
            [request()]    [handle()]
                  |
                  v
           [state->handle()]
                  |
                  v
         [可能改变当前状态]

3. 适用场景

应该使用状态模式的场景

​场景1:TCP连接状态管理​

// 状态接口
class TCPState {
public:
    virtual void open(TCPConnection*) = 0;
    virtual void close(TCPConnection*) = 0;
    // ...
};

// 上下文
class TCPConnection {
    std::unique_ptr<TCPState> state_;
public:
    void setState(std::unique_ptr<TCPState> state) {
        state_ = std::move(state);
    }
    // 委托给当前状态
    void open() { state_->open(this); }
    void close() { state_->close(this); }
};

​场景2:电梯控制系统​

class ElevatorState {
public:
    virtual void moveToFloor(int) = 0;
    virtual void openDoor() = 0;
    // ...
};

class Elevator {
    std::unique_ptr<ElevatorState> state_;
    int currentFloor_;
public:
    void changeState(std::unique_ptr<ElevatorState> newState) {
        state_ = std::move(newState);
    }
    void requestFloor(int floor) {
        state_->moveToFloor(floor);
    }
};

​场景3:订单状态处理​

class OrderState {
public:
    virtual void cancel(Order*) = 0;
    virtual void ship(Order*) = 0;
    // ...
};

class Order {
    std::unique_ptr<OrderState> state_;
public:
    void setState(std::unique_ptr<OrderState> state) {
        state_ = std::move(state);
    }
    void cancel() { state_->cancel(this); }
};

不适合使用状态模式的场景

​场景1:简单状态转换​

当状态数量很少(2-3个)且行为简单时,使用状态模式可能过度设计。

​替代方案​:使用枚举和switch语句

​场景2:状态频繁变化的高性能场景​

当状态转换非常频繁且性能敏感时,状态模式的对象创建开销可能成为瓶颈。

​替代方案​:使用状态表或位掩码等更轻量级方案

​场景3:状态逻辑高度耦合​

当不同状态的行为高度相似或相互依赖时,状态模式可能增加不必要的复杂性。

​替代方案​:考虑策略模式或模板方法模式

4. UML类图

持有当前状态
实现
实现
可能修改状态
Context
-state: State*
+request()
+changeState(State*)
«interface»
State
+handle(Context*)
ConcreteStateA
+handle(Context*)
ConcreteStateB
+handle(Context*)

5. 结构

模式参与者详细分析

​Context(上下文)​​

  • 角色:定义客户端感兴趣的接口

  • 职责:

    • 维护一个ConcreteState子类的实例

    • 将状态相关的请求委托给当前状态对象

    • 提供方法用于改变当前状态

​State(状态接口)​​

  • 角色:定义状态的通用接口

  • 职责:

    • 声明状态特定的行为方法

    • 可包含默认实现或纯虚函数

​ConcreteState(具体状态)​​

  • 角色:实现特定状态的行为

  • 职责:

    • 实现与状态相关的行为

    • 可触发状态转换(通过Context)

    • 可访问Context以获取所需信息

协作流程

  1. Context接收到客户端请求

  2. Context将请求委托给当前状态对象

  3. 状态对象处理请求,可能:

  • 执行状态特定的行为

  • 修改Context的状态(通过changeState)

  1. 返回处理结果给客户端

6. 代码示例

#include <iostream>
#include <memory>
#include <string>

// 前向声明
class Player;

// 状态接口
class PlayerState {
public:
    virtual ~PlayerState() = default;
    virtual void handleInput(Player& player, const std::string& input) = 0;
    virtual void update(Player& player) = 0;
    virtual std::string getDescription() const = 0;
};

// 具体状态:站立状态
class StandingState : public PlayerState {
public:
    void handleInput(Player& player, const std::string& input) override;
    void update(Player& player) override {
        std::cout << "Player is standing firmly\n";
    }
    std::string getDescription() const override {
        return "Standing";
    }
};

// 具体状态:跳跃状态
class JumpingState : public PlayerState {
public:
    void handleInput(Player& player, const std::string& input) override;
    void update(Player& player) override {
        std::cout << "Player is in the air\n";
    }
    std::string getDescription() const override {
        return "Jumping";
    }
};

// 具体状态:奔跑状态
class RunningState : public PlayerState {
public:
    void handleInput(Player& player, const std::string& input) override;
    void update(Player& player) override {
        std::cout << "Player is running fast\n";
    }
    std::string getDescription() const override {
        return "Running";
    }
};

// 上下文:玩家类
class Player {
    std::unique_ptr<PlayerState> state_;
public:
    Player() : state_(std::make_unique<StandingState>()) {}
    
    void setState(std::unique_ptr<PlayerState> newState) {
        state_ = std::move(newState);
        std::cout << "Player state changed to: " << state_->getDescription() << "\n";
    }
    
    void handleInput(const std::string& input) {
        state_->handleInput(*this, input);
    }
    
    void update() {
        state_->update(*this);
    }
    
    std::string getCurrentState() const {
        return state_->getDescription();
    }
};

// 状态处理实现
void StandingState::handleInput(Player& player, const std::string& input) {
    if (input == "press jump") {
        player.setState(std::make_unique<JumpingState>());
    } else if (input == "press run") {
        player.setState(std::make_unique<RunningState>());
    }
}

void JumpingState::handleInput(Player& player, const std::string& input) {
    if (input == "land") {
        player.setState(std::make_unique<StandingState>());
    }
}

void RunningState::handleInput(Player& player, const std::string& input) {
    if (input == "stop") {
        player.setState(std::make_unique<StandingState>());
    } else if (input == "press jump") {
        player.setState(std::make_unique<JumpingState>());
    }
}

// 客户端代码
int main() {
    Player player;
    
    std::cout << "Initial state: " << player.getCurrentState() << "\n";
    
    player.update();
    player.handleInput("press run");
    player.update();
    player.handleInput("press jump");
    player.update();
    player.handleInput("land");
    player.update();
    
    return 0;
}

7. 运行结果

Initial state: Standing
Player is standing firmly
Player state changed to: Running
Player is running fast
Player state changed to: Jumping
Player is in the air
Player state changed to: Standing
Player is standing firmly

​关键输出解释:​​

  1. 初始状态为Standing

  2. 处理"press run"输入后,状态转换为Running

  3. 处理"press jump"输入后,状态转换为Jumping

  4. 处理"land"输入后,状态恢复为Standing

  5. 每次状态转换后,update()方法输出当前状态特有的行为

8. 应用场景

实际项目中的应用案例

​cpp标准库​:

  • 文件流的状态管理(good/eof/fail/bad)

  • 正则表达式匹配器的状态

​游戏开发​:

  • 角色动画状态机

  • AI行为状态管理

  • 游戏流程控制(菜单、游戏、暂停等)

​系统编程​:

  • 线程状态管理

  • 网络协议状态机(如HTTP请求处理)

  • 设备驱动状态控制

行业特定应用

​嵌入式系统​:

  • 传感器状态管理

  • 低功耗模式切换

  • 硬件初始化流程

​高性能计算​:

  • 计算任务状态跟踪

  • 并行算法阶段管理

  • 数据处理流水线控制

9. 变体

主要变体实现

​基础变体​:经典GoF实现(如上例)

​状态表变体​:

class StateMachine {
    std::unordered_map<State, std::unordered_map<Event, State>> transitions;
    State current;
public:
    void handle(Event e) {
        if (transitions[current].count(e)) {
            current = transitions[current][e];
        }
    }
};

​模板化状态机​:

template<typename StateEnum>
class GenericStateMachine {
    StateEnum current;
    // 使用函数指针或std::function处理转换
};

变体对比表格

变体名称优点缺点适用场景
经典GoF实现面向对象,易扩展对象创建开销复杂状态逻辑
状态表高效,配置灵活不易调试大量简单状态
模板化类型安全,可复用编译时确定已知状态集

10. 状态模式 vs. 其他模式

与策略模式的区别

  • ​状态模式​:状态转换由状态对象控制,状态知道其他状态

  • ​策略模式​:策略通常不知道其他策略,转换由客户端控制

与命令模式的区别

  • ​状态模式​:关注对象内部状态变化

  • ​命令模式​:关注请求的封装和执行

选择指南

  • 当行为主要依赖状态时:选择状态模式

  • 当需要动态切换算法时:选择策略模式

  • 当需要撤销/重做功能时:选择命令模式

11. 状态模式的优缺点

优点分析

  1. ​消除条件分支​
  • 将状态相关行为局部化到状态类中

  • 避免大型switch/case语句

  • 示例:游戏角色行为代码更清晰

  1. ​易于扩展新状态​
  • 符合开闭原则

  • 添加新状态不影响现有代码

  • 示例:添加"蹲下"状态只需新增类

  1. ​状态转换显式化​
  • 转换逻辑集中在状态类中

  • 比分散的条件语句更易维护

缺点分析

  1. ​运行时开销​
  • 需要创建多个状态对象

  • 频繁转换可能影响性能

  • 缓解:使用状态对象池

  1. ​增加类数量​
  • 每个状态需要一个类

  • 可能导致类爆炸

  • 缓解:将简单状态合并

12. 什么时候不要使用状态模式?

明确的反对信号

  • ​状态数量很少​(<3个):简单枚举+switch更合适

  • ​状态无行为差异​:仅需标记状态时,使用flag变量

  • ​性能关键路径​:对象创建开销不可接受时

反模式警示

  1. ​上帝状态对象​
  • 一个状态类知道太多其他状态

  • 正确做法:状态只了解直接可达状态

  1. ​上下文膨胀​
  • 上下文类包含太多与状态无关的功能

  • 正确做法:保持上下文精简

  1. ​循环依赖​
  • 状态和上下文相互过度依赖

  • 正确做法:单向依赖(状态->上下文)

13. 总结

核心价值总结

状态模式是管理对象行为随状态变化的优雅解决方案,特别适合:

  • 复杂的状态转换逻辑

  • 需要频繁添加新状态的场景

  • 消除条件语句的需求

最佳实践建议

  1. 使用智能指针管理状态对象生命周期

  2. 考虑状态对象的复用(对象池)

  3. 保持状态转换逻辑集中

  4. 为状态机定义清晰的接口

  5. 编写单元测试覆盖所有状态转换

进阶学习路径

  1. 研究有限状态机(FSM)理论

  2. 学习层次状态机和状态图表

  3. 探索Boost.MSM等状态机库

  4. 实践更复杂的游戏AI状态管理

完整项目示例(CMake)

cmake_minimum_required(VERSION 3.15)
project(StatePatternExample)

set(CMAKE_CXX_STANDARD 17)

add_executable(state_pattern_example
    src/main.cpp
    src/player.cpp
    src/states.cpp
)

# 单元测试
enable_testing()
add_executable(state_pattern_tests
    test/test_states.cpp
)
target_link_libraries(state_pattern_tests PRIVATE gtest_main)
add_test(NAME state_pattern_tests COMMAND state_pattern_tests)

​单元测试示例(Google Test)​​:

#include "gtest/gtest.h"
#include "../src/player.h"
#include "../src/states.h"

TEST(PlayerStateTest, InitialState) {
    Player player;
    EXPECT_EQ(player.getCurrentState(), "Standing");
}

TEST(PlayerStateTest, RunningTransition) {
    Player player;
    player.handleInput("press run");
    EXPECT_EQ(player.getCurrentState(), "Running");
}

TEST(PlayerStateTest, JumpFromRunning) {
    Player player;
    player.handleInput("press run");
    player.handleInput("press jump");
    EXPECT_EQ(player.getCurrentState(), "Jumping");
}

通过状态模式,可以创建出清晰、可维护的状态管理系统,特别适合cpp中需要管理复杂对象行为的场景。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐