C++实现设计模式---状态模式 (State)

状态模式 (State)

状态模式 是一种行为型设计模式,它允许对象在运行时根据内部状态的改变来动态改变其行为。通过将状态相关的行为封装到独立的类中,状态模式使得状态的切换更加清晰和灵活。


意图

  • 将对象的行为和状态分离,随着状态的改变动态调整对象的行为。
  • 避免使用大量的 if-else 或 switch-case 语句处理状态逻辑。

使用场景

  1. 对象的行为依赖于状态 :
    • 例如订单的状态(未支付、已支付、已发货)。
  2. 需要动态切换状态 :
    • 例如游戏角色的动作(站立、行走、奔跑)。
  3. 需要扩展状态逻辑 :
    • 新增状态时不影响现有逻辑。

参与者角色

  1. 上下文类 (Context)
    • 保存当前状态的引用,提供接口供外部访问,并委托状态对象执行行为。
  2. 状态接口 (State)
    • 定义状态的公共接口。
  3. 具体状态类 (ConcreteState)
    • 实现状态接口并定义该状态下的具体行为。

代码示例

以下代码展示了一个机器人(Robot)的状态模式实现。机器人可以处于"待机状态 (Idle)"、"工作状态 (Working)"和"充电状态 (Charging)"之间切换。

cpp 复制代码
#include <iostream>
#include <memory>
#include <string>

// ------------------------------
// 状态接口:定义机器人状态的公共接口
class RobotContext; // 前置声明上下文类

class RobotState {
public:
    virtual ~RobotState() = default; // 虚析构函数,确保多态销毁

    // 状态行为接口
    virtual void startWork(RobotContext& context) = 0;     // 开始工作
    virtual void chargeBattery(RobotContext& context) = 0; // 开始充电
    virtual std::string getStateName() const = 0;          // 获取状态名称
};

// ------------------------------
// 上下文类:机器人
class RobotContext {
private:
    std::unique_ptr<RobotState> state; // 当前机器人状态,使用 unique_ptr 管理状态对象

public:
    explicit RobotContext(std::unique_ptr<RobotState> initialState) 
        : state(std::move(initialState)) {}

    // 设置新的状态
    void setState(std::unique_ptr<RobotState> newState) {
        state = std::move(newState);
    }

    // 委托开始工作行为给当前状态
    void startWork() {
        state->startWork(*this);
    }

    // 委托充电行为给当前状态
    void chargeBattery() {
        state->chargeBattery(*this);
    }

    // 获取当前状态名称
    std::string getStateName() const {
        return state->getStateName();
    }
};

// ------------------------------
// 具体状态类:工作状态
class WorkingState : public RobotState {
public:
    void startWork(RobotContext& context) override {
        std::cout << "机器人已经在工作中,无法重复开始工作。
";
    }

    void chargeBattery(RobotContext& context) override {
        std::cout << "机器人停止工作,切换到充电状态。
";
        context.setState(std::make_unique<class ChargingState>()); // 切换到"充电状态"
    }

    std::string getStateName() const override {
        return "工作状态";
    }
};

// ------------------------------
// 具体状态类:充电状态
class ChargingState : public RobotState {
public:
    void startWork(RobotContext& context) override {
        std::cout << "机器人充电完成,切换到工作状态。
";
        context.setState(std::make_unique<class WorkingState>()); // 切换到"工作状态"
    }

    void chargeBattery(RobotContext& context) override {
        std::cout << "机器人已经在充电中,无法重复充电。
";
    }

    std::string getStateName() const override {
        return "充电状态";
    }
};

// ------------------------------
// 具体状态类:待机状态
class IdleState : public RobotState {
public:
    void startWork(RobotContext& context) override {
        std::cout << "机器人开始工作,切换到工作状态。
";
        context.setState(std::make_unique<class WorkingState>()); // 切换到"工作状态"
    }

    void chargeBattery(RobotContext& context) override {
        std::cout << "机器人开始充电,切换到充电状态。
";
        context.setState(std::make_unique<class ChargingState>()); // 切换到"充电状态"
    }

    std::string getStateName() const override {
        return "待机状态";
    }
};

// ------------------------------
// 客户端代码
int main() {
    // 初始化机器人为"待机状态"
    RobotContext robot(std::make_unique<IdleState>());

    // 获取当前状态并输出
    std::cout << "当前机器人状态: " << robot.getStateName() << "
";

    // 机器人开始工作
    robot.startWork();
    std::cout << "当前机器人状态: " << robot.getStateName() << "
";

    // 机器人充电
    robot.chargeBattery();
    std::cout << "当前机器人状态: " << robot.getStateName() << "
";

    // 再次开始工作
    robot.startWork();
    std::cout << "当前机器人状态: " << robot.getStateName() << "
";

    return 0;
}

代码解析

1. 状态接口 (RobotState)
  • 定义公共接口 :
    • 包括 startWork、chargeBattery 和 getStateName 三个接口。
    • 每个具体状态类都必须实现这些接口,定义具体的行为。
2. 上下文类 (RobotContext)
  • 负责管理当前状态 :
    • RobotContext 保存当前状态的智能指针(std::unique_ptr),并负责管理状态对象的生命周期。
  • 行为委托 :
    • 调用 startWork 或 chargeBattery 时,行为委托给当前状态对象。
  • 状态切换 :
    • 通过 setState 方法切换状态,使用 std::make_unique 创建新的状态对象。
3. 具体状态类
  • IdleState (待机状态) :
    • 允许切换到"工作状态"或"充电状态"。
  • WorkingState (工作状态) :
    • 如果重复调用 startWork,提示"已在工作中"。
    • 支持切换到"充电状态"。
  • ChargingState (充电状态) :
    • 如果重复调用 chargeBattery,提示"已在充电中"。
    • 支持切换到"工作状态"。
4. 状态切换逻辑
  • 每个状态类都可以通过 context.setState(std::make_unique<NewState>()); 切换到其他状态。
  • 切换状态时使用 std::unique_ptr 管理状态对象,确保内存安全。

运行结果

运行程序后,输出如下:

复制代码
当前机器人状态: 待机状态
机器人开始工作,切换到工作状态。
当前机器人状态: 工作状态
机器人停止工作,切换到充电状态。
当前机器人状态: 充电状态
机器人充电完成,切换到工作状态。
当前机器人状态: 工作状态

优缺点

优点
  1. 封装状态逻辑 :
    • 每个状态的行为封装在独立的类中,方便维护和扩展。
  2. 动态切换状态 :
    • 通过 setState 动态切换状态,逻辑清晰。
  3. 消除条件语句 :
    • 使用多态机制避免了大量的 if-else 或 switch-case 语句。
缺点
  1. 类数量增加 :
    • 每种状态都需要单独定义一个类,可能导致类数量较多。
  2. 上下文和状态耦合 :
    • 状态类依赖上下文类的操作。

适用场景

  1. 对象的行为依赖于状态 :
    • 如订单状态、文件操作等。
  2. 需要动态切换状态 :
    • 系统需要根据当前状态动态改变行为。

总结

状态模式通过将状态的行为封装到独立的类中,实现了对象的动态行为调整,是有限状态机的优雅实现方式。它特别适用于需要频繁切换状态且每种状态具有独立逻辑的场景。

相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
weilx12343 天前
C++笔记-文件IO-<fcntl.h>
c++
Smileyqp沛沛3 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·3 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁3 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
程序猿编码3 天前
告别改源码适配模型:纯 C++ 可配置 LLM 推理引擎,全格式全结构兼容
c++·大模型·llm·推理引擎
此生决int3 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++