状态模式(State Pattern)

C++ 状态模式(State Pattern)

一、模式概述

状态模式(State Pattern)属于行为型设计模式

核心思想

将对象的不同状态 分别封装为独立类,对象行为随内部状态改变而自动变化,对外表现如同修改了对象所属类;彻底替代传统大量 if-else / switch 分支判断,让状态逻辑解耦、职责清晰。

适用场景

  1. 对象行为依赖自身状态,运行时状态改变则行为随之改变;
  2. 业务中存在大量基于状态的条件判断代码,分支臃肿、难以维护;
  3. 状态数量固定、状态转换规则明确,需要扩展新状态;
  4. 典型业务:电梯状态、订单流程、网络连接、游戏角色、设备运行状态机等。

二、核心角色

状态模式由三类核心角色组成,各司其职:

角色 名称 职责说明
抽象状态 State 定义所有状态统一的行为接口,声明状态处理方法,通常为纯虚类
具体状态 ConcreteStateX 继承抽象状态,实现当前状态独有行为;负责触发状态跳转
上下文 Context 持有当前状态对象;对外提供统一调用入口;提供状态切换接口,串联整个状态流程

角色协作流程

  1. 客户端调用上下文的对外接口;
  2. 上下文将请求委托给当前具体状态对象处理;
  3. 具体状态执行自身逻辑,并根据业务规则修改上下文的状态;
  4. 下次请求会交由新状态处理,实现行为自动切换。

三、模式优缺点

优点

  1. 消除分支语句 :剥离大量 if-else/switch,代码结构更整洁;
  2. 符合开闭原则:新增状态只需新增具体状态类,无需修改原有代码;
  3. 状态逻辑隔离:每个状态的行为、转换逻辑内聚在对应类中,职责单一,易维护;
  4. 状态转换规范化:转换规则统一管理,流程清晰可控。

缺点

  1. 类数量膨胀:状态越多,对应的具体状态类就越多,增加代码体量;
  2. 流程分散:状态跳转逻辑分散在各个状态类中,整体状态流转流程阅读难度提升;
  3. 过度设计:简单状态(仅2~3个状态、逻辑简单)使用该模式会增加复杂度。

四、C++ 基础实现(标准写法)

1. 前置说明

  • 使用纯虚类作为抽象状态基类;
  • 采用原始指针演示基础逻辑,后文补充智能指针工程写法;
  • 类之间存在交叉引用,使用前向声明解决头文件循环依赖。

2. 完整代码示例

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

// 前向声明上下文类
class Context;

// ===================== 抽象状态类 State =====================
class State
{
public:
    // 纯虚函数:状态处理行为,参数为上下文,用于状态切换
    virtual void handle(Context* context) = 0;
    virtual ~State() = default; // 虚析构,保证多态析构安全
};

// ===================== 具体状态类 A =====================
class ConcreteStateA : public State
{
public:
    void handle(Context* context) override;
};

// ===================== 具体状态类 B =====================
class ConcreteStateB : public State
{
public:
    void handle(Context* context) override;
};

// ===================== 上下文类 Context =====================
class Context
{
private:
    State* m_currentState; // 持有当前状态对象
public:
    // 构造函数:初始化默认状态
    Context(State* state) : m_currentState(state) {}

    // 设置/切换状态
    void setState(State* state)
    {
        m_currentState = state;
    }

    // 对外统一请求接口
    void request()
    {
        if (m_currentState)
        {
            m_currentState->handle(this); // 委托给当前状态处理
        }
    }
};

// 状态A 实现逻辑:执行行为,并切换到状态B
void ConcreteStateA::handle(Context* context)
{
    cout << "当前处于【状态A】,执行状态A逻辑" << endl;
    // 状态跳转:切换为状态B
    context->setState(new ConcreteStateB());
}

// 状态B 实现逻辑:执行行为,并切换到状态A
void ConcreteStateB::handle(Context* context)
{
    cout << "当前处于【状态B】,执行状态B逻辑" << endl;
    // 状态跳转:切换为状态A
    context->setState(new ConcreteStateA());
}

// ===================== 客户端测试 =====================
int main()
{
    // 初始化上下文,默认状态为A
    Context ctx(new ConcreteStateA());

    // 多次调用,观察状态自动切换
    ctx.request();
    ctx.request();
    ctx.request();

    return 0;
}

3. 运行结果

复制代码
当前处于【状态A】,执行状态A逻辑
当前处于【状态B】,执行状态B逻辑
当前处于【状态A】,执行状态A逻辑

五、C++ 工程级优化(智能指针版)

原始指针容易造成内存泄漏、野指针 ,正式项目推荐使用 std::unique_ptr 管理状态对象。

优化后代码

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

class Context;

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

// 具体状态A
class ConcreteStateA : public State
{
public:
    void handle(Context* context) override;
};

// 具体状态B
class ConcreteStateB : public State
{
public:
    void handle(Context* context) override;
};

// 上下文(使用 unique_ptr 管理状态)
class Context
{
private:
    unique_ptr<State> m_currentState;
public:
    explicit Context(unique_ptr<State> state) : m_currentState(move(state)) {}

    void setState(unique_ptr<State> state)
    {
        m_currentState = move(state);
    }

    void request()
    {
        if (m_currentState)
            m_currentState->handle(this);
    }
};

void ConcreteStateA::handle(Context* context)
{
    cout << "当前处于【状态A】,执行逻辑" << endl;
    context->setState(make_unique<ConcreteStateB>());
}

void ConcreteStateB::handle(Context* context)
{
    cout << "当前处于【状态B】,执行逻辑" << endl;
    context->setState(make_unique<ConcreteStateA>());
}

// 客户端
int main()
{
    Context ctx(make_unique<ConcreteStateA>());
    ctx.request();
    ctx.request();
    ctx.request();
    return 0;
}

优势 :智能指针自动释放内存,无需手动 delete,规避内存泄漏。

六、两种状态切换方式

在实际开发中,状态切换分为两种模式:

1. 状态类内部控制跳转(常用)

  • 跳转逻辑写在 ConcreteState::handle 中;
  • 优点:状态自管理,内聚性强;
  • 缺点:跳转逻辑分散。

2. 上下文统一控制跳转

  • 所有状态转换规则集中写在 Context 中;
  • 优点:整体状态流转一目了然,便于梳理完整流程;
  • 缺点:上下文会随状态增多变得臃肿。

七、状态模式 vs 策略模式(易混区分)

两者结构相似,均封装行为、委托执行,核心区别在行为目的与切换逻辑

对比维度 状态模式 策略模式
核心目的 处理对象状态变化,行为随状态自动改变 封装多种算法/策略,按需替换算法
切换触发 内部状态流转,自动切换 外部客户端主动选择,手动切换
关系 状态之间存在流转、依赖关系 各个策略相互独立,无先后流转关系
典型场景 状态机、订单、电梯、设备状态 排序算法、支付方式、加密算法

八、使用注意事项

  1. 控制状态数量:状态过多会产生大量子类,若状态超过10个,可考虑改用配置表+枚举实现状态机;
  2. 循环依赖处理ContextState 互相引用,必须使用前向声明
  3. 析构安全 :抽象状态基类必须提供虚析构函数,防止多态析构内存泄漏;
  4. 慎用场景:简单两状态、固定逻辑,不建议强行使用,避免过度设计;
  5. 多线程场景 :多线程下需对 m_currentState 加锁,保证状态切换线程安全。

九、总结

  1. 本质:把状态变成类,用多态替代分支判断
  2. 核心三角色:抽象状态、具体状态、上下文;
  3. C++ 开发优先使用智能指针管理状态对象,保证内存安全;
  4. 适合复杂状态机场景,简单场景按需取舍。
相关推荐
UXbot15 小时前
如何选择适合公司项目的UI设计工具?企业选型指南
前端·低代码·ui·团队开发·原型模式·设计规范·web app
UXbot19 小时前
原型设计工具如何帮助新人快速进入产品行业?
前端·低代码·ui·交互·团队开发·原型模式·web app
烈焰晴天1 天前
Codex 桌面端如何链接Figma MCP 服务器拿到 Figma设计稿精准尺寸等结构化数据 来精准还原UI
服务器·ui·figma
狼哥16861 天前
防沉迷控制实战新特性接入
ui·华为·harmonyos
狼哥16861 天前
学习卡片案例新特性接入
ui·华为·harmonyos
zdr尽职尽责1 天前
Unity录像功能
学习·ui·unity·游戏引擎
山东布谷网络科技1 天前
海外直播语聊APP功能与UI升级的关键关注点
开发语言·ui·app store·谷歌上架·海外直播app开发·海外语聊平台搭建·多语言直播平台定制
鹤卿1232 天前
(OC)UI学习——网易云仿写
ui·ios·objective-c
colofullove2 天前
实时游玩页与 WebSocket 状态管理实现
websocket·网络协议·状态模式
一个被程序员耽误的厨师2 天前
04-实践篇-让AI生成可视化页面-ai-json-ui的落地实践
人工智能·ui·json