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

状态模式 (State)

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


意图

  • 将对象的行为和状态分离,随着状态的改变动态调整对象的行为。
  • 避免使用大量的 if-elseswitch-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)
  • 定义公共接口
    • 包括 startWorkchargeBatterygetStateName 三个接口。
    • 每个具体状态类都必须实现这些接口,定义具体的行为。
2. 上下文类 (RobotContext)
  • 负责管理当前状态
    • RobotContext 保存当前状态的智能指针(std::unique_ptr),并负责管理状态对象的生命周期。
  • 行为委托
    • 调用 startWorkchargeBattery 时,行为委托给当前状态对象。
  • 状态切换
    • 通过 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-elseswitch-case 语句。
缺点
  1. 类数量增加
    • 每种状态都需要单独定义一个类,可能导致类数量较多。
  2. 上下文和状态耦合
    • 状态类依赖上下文类的操作。

适用场景

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

总结

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

相关推荐
skywalk816321 分钟前
C语言基本知识复习浓缩版:数组
c语言·数据结构·c++·算法
GHL2842710901 小时前
面试:类模版中函数声明在.h,定义在.cpp中,其他cpp引用引入这个头文件,会有什么错误?
c++·学习
IOT-Power1 小时前
<C++学习>C++ Boost 输入与输出教程
c++
Victoria.a2 小时前
数据在内存的存储
c语言·c++
#include>2 小时前
【C++boost::asio网络编程】使用asio协程搭建异步echo服务器的笔记
服务器·网络·c++
arong_xu3 小时前
深入解析 C++ 类型转换
开发语言·c++·类型转换·casting
labuladuo5203 小时前
洛谷 P3435 [POI2006] OKR-Periods of Words(扩展KMP+线段树做法)
数据结构·c++·算法
不想睡觉_3 小时前
21天学通C++第八章——指针
开发语言·c++
我叫王棱夹4 小时前
acwing-3194 最大的矩形
数据结构·c++·算法
Lenyiin5 小时前
第432场周赛:跳过交替单元格的之字形遍历、机器人可以获得的最大金币数、图的最大边权的最小值、统计 K 次操作以内得到非递减子数组的数目
c++·算法·leetcode·周赛·lenyiin