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;
}
  • 优点:
    • 每个状态只关心自己的逻辑,代码分散管理
    • 扩展新状态只要加一个类/结构,不用改其他状态代码
    • 更容易阅读和维护
  • 缺点:
    • 相比单个函数,要定义更多对象(初期代码量稍大)
相关推荐
吃一碗粉只付一碗钱7 小时前
12-给SPA友好的401和403响应
c#·状态模式
sunlifenger20 小时前
嵌入式部署,解锁张家港智慧灯杆的“极简组网”密码
科技·状态模式
上火的金鱼妹2 天前
关于多进程sse连接通知的实战
javascript·状态模式·sse
阿俊-全栈开发2 天前
LikeShop 小程序商城首页可配置弹窗的设计与实现(Java + uni-app)
小程序·状态模式·likeshop·likeshop开源商城
lingyubb2 天前
配音工具怎么选?从 TTS 合成链路看 6 个可量化参数
状态模式
平头哥技术团队4 天前
从预订问到退房:一间山野民宿的数字人「旅程地图」——Theme07_Homestay.vue 端到端拆解
状态模式·数字人
未来之窗软件服务5 天前
web网络请求竞态问题与取消方案-东方仙盟
前端·状态模式·仙盟创梦ide·东方仙盟·网络取消
一级新生6 天前
多智能体协作:常用协作模式详解
人工智能·状态模式·agent·多智能体·智能体
喜欢打篮球的普通人8 天前
LLVM 前端与优化层:Clang 如何把 C 代码变成 IR
c语言·前端·状态模式
m0_敲代码的彭于晏1688 天前
Vercel 前端部署与全栈开发实战指南
前端·状态模式