首页 物联网

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)

分类:物联网
字数: (4801)
阅读: (3749)
内容摘要:C++设计模式精讲:状态模式应用与避坑指南(附完整代码),

在日常开发中,我们经常会遇到这样的场景:一个对象的行为取决于它的状态,并且需要在运行时根据不同的状态执行不同的操作。如果直接使用大量的 if-else 或者 switch-case 语句来处理状态转换,会导致代码臃肿、可读性差,并且难以维护。例如,一个TCP连接,可能存在 listening, established, closed等状态,每种状态的处理逻辑都有所不同。这就是状态模式要解决的核心问题。它允许一个对象在其内部状态改变时改变它的行为,对象看起来好像修改了它的类。

问题场景重现:简易电梯系统

假设我们要设计一个简单的电梯系统。电梯有以下几种状态:

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)
  • 开门 (Opening)
  • 关门 (Closing)
  • 运行 (Running)
  • 停止 (Stopping)

每种状态下,电梯可以执行不同的操作,比如开门状态下只能关门,运行状态下只能停止。如果我们使用 if-else 来实现,代码会变得非常复杂。

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)

状态模式的底层原理

状态模式的核心思想是将对象的每一种状态封装成独立的类,称为“状态类”。Context 类维护一个指向当前状态的引用,并将客户端的请求委托给当前状态对象处理。当状态需要切换时,Context 类只需要更换其内部持有的状态对象即可。这样,每个状态类只负责处理自身状态下的行为,避免了代码的耦合和冗余。

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)

状态模式主要包含以下几个角色:

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)
  • Context(环境类):维护一个 ConcreteState 子类的实例,这个实例定义当前状态。Context 将客户端的请求委托给当前状态对象处理。
  • State(抽象状态类):定义一个接口以封装与 Context 的特定状态相关的行为。
  • ConcreteState(具体状态类):实现 State 接口,处理来自 Context 的请求。每个 ConcreteState 都实现了与自身状态相关的行为。

C++ 代码实现:电梯状态管理

以下是使用 C++ 实现电梯状态模式的示例代码:

// 抽象状态类
class State {
public:
    virtual void openDoor() {} // 默认空实现
    virtual void closeDoor() {}
    virtual void run() {}
    virtual void stop() {}
    virtual ~State() {}
};

// 具体状态类:开门状态
class OpeningState : public State {
public:
    void closeDoor() override {
        std::cout << "开门状态:关门...\n";
        // 切换到关门状态(需要访问 Context,这里简化处理)
        //context->setState(context->closingState);
    }
};

// 具体状态类:关门状态
class ClosingState : public State {
public:
    void run() override {
        std::cout << "关门状态:运行...\n";
        // 切换到运行状态
        //context->setState(context->runningState);
    }
};

// 具体状态类:运行状态
class RunningState : public State {
public:
    void stop() override {
        std::cout << "运行状态:停止...\n";
        // 切换到停止状态
        //context->setState(context->stoppingState);
    }
};

// 具体状态类:停止状态
class StoppingState : public State {
public:
    void openDoor() override {
        std::cout << "停止状态:开门...\n";
        // 切换到开门状态
        //context->setState(context->openingState);
    }
};

// 环境类:电梯
class Context {
public:
    State* openingState;
    State* closingState;
    State* runningState;
    State* stoppingState;
    State* currentState;

    Context() : openingState(new OpeningState()), closingState(new ClosingState()),
                  runningState(new RunningState()), stoppingState(new StoppingState()),
                  currentState(stoppingState) {} // 初始状态设置为停止状态

    void setState(State* state) {
        currentState = state;
    }

    void openDoor() {
        currentState->openDoor();
    }

    void closeDoor() {
        currentState->closeDoor();
    }

    void run() {
        currentState->run();
    }

    void stop() {
        currentState->stop();
    }

    ~Context() {
        delete openingState;
        delete closingState;
        delete runningState;
        delete stoppingState;
    }
};

int main() {
    Context elevator;
    elevator.openDoor(); // 停止状态 -> 开门
    elevator.closeDoor(); // 开门状态 -> 关门
    elevator.run();       // 关门状态 -> 运行
    elevator.stop();      // 运行状态 -> 停止
    return 0;
}

实战避坑经验总结

  1. 状态转换图:在实现状态模式之前,务必绘制清晰的状态转换图,明确每个状态之间的转换关系,避免状态转换逻辑出现错误。
  2. 状态对象管理:状态对象可以是单例的,也可以是每次都创建新的。如果状态对象不包含任何实例变量,可以考虑使用单例模式,减少内存开销。但如果状态对象需要维护一些状态相关的数据,则需要每次都创建新的对象。
  3. Context 与 State 的交互:Context 需要持有所有 State 对象的引用,并且需要在 State 对象中提供切换状态的接口。可以使用依赖注入的方式,将 Context 对象传递给 State 对象,或者使用友元类的方式,允许 State 对象访问 Context 的私有成员。
  4. 避免循环依赖:Context 依赖 State,而 State 又可能需要依赖 Context 进行状态切换,这可能导致循环依赖。需要谨慎设计它们之间的关系,可以使用接口或者回调函数来解耦。
  5. 状态爆炸:如果状态过多,会导致状态类的数量急剧增加,增加代码的复杂性。需要仔细分析业务场景,合并相似的状态,或者使用状态表等技术来简化状态管理。

状态模式是处理复杂状态转换的利器,但需要根据实际情况进行灵活应用。合理使用状态模式可以提高代码的可维护性和可扩展性,避免代码的混乱和冗余。

C++设计模式精讲:状态模式应用与避坑指南(附完整代码)

转载请注明出处: 键盘上的咸鱼

本文的链接地址: http://m.acea4.store/blog/480077.SHTML

本文最后 发布于2026-04-16 06:21:36,已经过了11天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 小明同学 3 天前
    电梯这个例子很形象,一下子就理解了状态模式的用意。之前在搞游戏AI的时候也用过,效果不错。
  • 选择困难症 6 天前
    想问下,如果状态特别多,比如超过 20 个,状态模式是不是就不太适用了?有什么其他的方案可以考虑吗?
  • 背锅侠 2 天前
    想问下,如果状态特别多,比如超过 20 个,状态模式是不是就不太适用了?有什么其他的方案可以考虑吗?
  • 绿茶观察员 4 天前
    想问下,如果状态特别多,比如超过 20 个,状态模式是不是就不太适用了?有什么其他的方案可以考虑吗?