c++注意点(15)----状态模式

1. 核心定义

状态模式(State Pattern)

当一个对象的内部状态改变时,它的行为也随之改变,看起来就好像这个对象改变了它的类。

它的目标是 把状态的行为封装到独立的类/函数里,对象会在运行过程中切换引用的状态对象而改变自身行为。

2.为什么要用状态模式(State Pattern)

状态模式的核心目标是 管理对象在不同状态下的行为,并且 避免臃肿的条件分支。

背后的问题:

在很多程序(尤其是嵌入式设备、UI逻辑、协议处理)里,都有"状态"这个概念:

  • 一个设备可能处于 初始化 / 运行 / 故障 等状态
  • 每个状态下的操作(事件响应)不同
  • 如果我们直接用 switch-case 或 if-else 来判断状态,那么所有状态的逻辑会混在一起
    • 维护困难
    • 状态变多时 switch 会变得非常庞大
    • 扩展新状态需要改原有代码

状态模式就是为了解决这个可维护性和扩展性问题:

  • 把状态对应的行为封装成独立模块(类/结构/函数)
  • 当状态变化时,只需要修改当前引用的状态对象,不需要在大量的分支中找逻辑

3.状态模式的优点

  • 遵循开闭原则

不修改已有状态类,就能增加新状态(只是多一个实现类/函数)。

  • 清晰分离状态逻辑

每个状态的代码放在独立文件/类里,不会让 switch 把所有逻辑混在一起。

  • 运行时灵活切换状态

可以根据条件在运行中替换当前状态对象,立刻获得另一套行为。

  • 便于单元测试

每个状态可以单独测试,而不用在庞大的分支里模拟环境。

  • 可与多态结合(在 C++/Java 里)

用多态让调用方无需关心当前状态类型,直接调用统一接口。

4.状态模式和状态机的关系与区别

状态模式 ≠ 状态机 ,但它们关系很密切。

状态模式是一种"面向对象的设计方法",状态机是一种"系统建模与逻辑执行方式"。

对比项 状态模式 状态机
定义 把对象的状态和对应行为封装成独立类/对象 使用图/表表示系统的状态及状态之间的迁移
目的 避免复杂分支结构,使代码更易维护和扩展 精确描述和控制状态变化逻辑
实现形式 面向对象语言通常用多态;C 可用函数指针 既可用多态,也可用 switch、表驱动等
关注点 封装行为,减少依赖 状态、事件、迁移过程控制
场景 OOP 业务逻辑设计 嵌入式设备控制、协议解析、工作流程

联系

  • 一个状态机可以用"状态模式"来实现
  • 状态模式是实现状态机的一种优雅方法
  • 像 QP 框架就是在状态模式的基础上做了扩展:不仅封装状态行为,而且支持层次化状态机(HSM)、时间事件、事件队列等。

5. 和 switch-case 的区别

虽然 switch 也能实现状态控制,但差别很大:

  • 优点:简单、上手快
  • 缺点:
    • 所有状态逻辑集中在一个函数中,代码会越来越庞大
    • 新增状态需要修改这个 switch 结构 → 破坏开闭原则
    • 状态切换和行为执行往往耦合在一起,维护不方便
    • 不适合多人协作,每个状态都得修改同一个文件

用状态模式实现

c版本

cpp 复制代码
#include <stdio.h>

/* 状态接口函数指针定义 */
typedef struct State {
    void (*handle)(struct State **ctx); // 事件处理函数
} State;

/* 前向声明状态对象 */
void Init_handle(State **ctx);
void Run_handle(State **ctx);
void Error_handle(State **ctx);

/* 状态对象定义 */
State InitState  = { Init_handle  };
State RunState   = { Run_handle   };
State ErrorState = { Error_handle };

/* 状态机上下文(持有当前状态指针) */
typedef struct {
    State *current;
} Context;

/* 状态切换方法 */
void changeState(Context *ctx, State *newState) {
    ctx->current = newState;
}

/* 状态具体处理逻辑 */
void Init_handle(State **ctx) {
    printf("[INIT] 初始化完成,切到运行模式\n");
    *ctx = &RunState;
}

void Run_handle(State **ctx) {
    printf("[RUN] 设备正在运行,出现故障!\n");
    *ctx = &ErrorState;
}

void Error_handle(State **ctx) {
    printf("[ERROR] 错误处理完成,返回初始化\n");
    *ctx = &InitState;
}

/* 测试入口 */
int main(void) {
    Context ctx = { &InitState };

    for (int i = 0; i < 6; i++) {
        ctx.current->handle(&ctx.current); // 按一次按钮
    }
    return 0;
}

c++版本

cpp 复制代码
#include <iostream>
using namespace std;

class Context; // 前向声明

/* 抽象状态接口 */
class State {
public:
    virtual ~State() {}
    virtual void handle(Context* ctx) = 0;
};

/* 上下文类,持有当前状态 */
class Context {
public:
    Context(State* s) : current(s) {}
    void setState(State* s) { current = s; }
    void request() { current->handle(this); }
private:
    State* current;
};

/* 具体状态类 */
class InitState : public State {
public:
    void handle(Context* ctx) override;
};
class RunState : public State {
public:
    void handle(Context* ctx) override;
};
class ErrorState : public State {
public:
    void handle(Context* ctx) override;
};

/* 状态方法实现 */
void InitState::handle(Context* ctx) {
    cout << "[INIT] 初始化完成,切到运行模式\n";
    static RunState run;
    ctx->setState(&run);
}
void RunState::handle(Context* ctx) {
    cout << "[RUN] 设备正在运行,出现故障!\n";
    static ErrorState err;
    ctx->setState(&err);
}
void ErrorState::handle(Context* ctx) {
    cout << "[ERROR] 错误处理完成,返回初始化\n";
    static InitState init;
    ctx->setState(&init);
}

/* 测试入口 */
int main() {
    static InitState init; // 初始状态
    Context ctx(&init);

    for (int i = 0; i < 6; i++) {
        ctx.request();
    }
    return 0;
}
  • 优点:
    • 每个状态只关心自己的逻辑,代码分散管理
    • 扩展新状态只要加一个类/结构,不用改其他状态代码
    • 更容易阅读和维护
  • 缺点:
    • 相比单个函数,要定义更多对象(初期代码量稍大)
相关推荐
shi57836 小时前
设计模式之 状态机 C#范例
设计模式·状态模式
顾漂亮3 天前
Token快过期的三种续期方案
java·spring·状态模式
造价女工3 天前
视频监控系统原理与计量
网络·音视频·状态模式·消防·工程造价
暖锋丫4 天前
年会抽奖系统
状态模式
尘似鹤5 天前
设计一个状态机
学习·状态模式·嵌入式软件
Knight_AL5 天前
大文件分片上传:简单案例(前端切割与后端合并)
前端·状态模式
阿珊和她的猫8 天前
深入理解与手写发布订阅模式
开发语言·前端·javascript·vue.js·ecmascript·状态模式
Mr_WangAndy8 天前
C++设计模式_行为型模式_状态模式State
c++·设计模式·状态模式
zl9798999 天前
SpringBoot-依赖管理和自动配置
spring boot·后端·状态模式