C++设计模式——State状态模式

一,状态模式的定义

状态模式是一种行为型设计模式,状态模式允许对象在内部状态发生切换时改变它自身的行为。

状态模式的主要目的是将复杂的状态切换逻辑抽象化为一组离散的状态类,使代码结构更加清晰和易于维护。

状态模式将对象的行为封装到不同的状态类中,从而在应用程序的状态发生改变时,会自动切换到对应的状态类。状态模式使得状态的切换被表现为类对象的切换。

状态模式在现实生活中的抽象实例:

交通信号灯:交通信号灯有红灯、绿灯等多种状态。每个状态都定义了不同的行驶规则。

购物流程:用户在购物流程中有多种状态,例如浏览商品、添加购物车、填写收货地址等。

游戏角色:游戏角色可以处于不同的状态,例如行走、攻击等,玩家可以让角色在不同状态间切换。

订单状态:每个订单包含待支付、已支付、已发货、已完成等多个状态。

二,状态模式的结构

状态模式主要包含以下组件:

1.状态上下文(Context):

状态上下文是一个持有状态对象的类,它持有一个状态对象的引用,对外提供了切换状态的统一接口。

2.抽象状态(State):

它定义了在特定状态下对象的行为,声明了对象在某状态下的操作。

3.具体状态(Concrete State):

包含对抽象状态的具体实现。每个具体状态类代表着一种具体状态,并包含了该状态对应的具体操作。

组件之间的工作步骤如下:

1.初始化状态对象,利用状态对象来初始化状态上下文。

2.状态上下文设置当前状态。

3.状态上下文调用当前状态对应的处理逻辑。

4.状态上下文开始切换状态,并引用另一个状态对象。

对应UML类图:

三,状态模式代码样例

cpp 复制代码
#include <iostream>

class State {
public:
    virtual ~State() {}
    virtual void handle() = 0;
};

class ConcreteStateA: public State {
public:
    void handle() override {
        std::cout << "Handling state A" << std::endl;
    }
};

class ConcreteStateB: public State {
public:
    void handle() override {
        std::cout << "Handling state B" << std::endl;
    }
};

class Context {
private:
    State* state;
public:
    Context(State* state) : state(state) {}
    ~Context() {
        delete state;
    }
    void setState(State* state) {
        delete this->state;
        this->state = state;
    }
    void request() {
        state->handle();
    }
};

int main() {
    State* stateA = new ConcreteStateA();
    State* stateB = new ConcreteStateB();

    Context context(stateA);
    context.request();

    context.setState(stateB);
    context.request();

    return 0;
}

运行结果:

bash 复制代码
Handling state A
Handling state B

四,状态模式的应用场景

网络管理:网络编程中经常涉及多种网络状态切换,比如发送请求、断开连接等。

分布式系统:分布式系统的节点可能有多种工作状态,比如就绪、运行、故障恢复等。

游戏开发:游戏角色的行为可能会随着生命值、等级、装备的不同而变化。

图形界面开发:在GUI应用中,组件有多种状态,比如按钮有"正常"、"按下"等状态。

五,状态模式的优缺点

状态模式的优点:

修改灵活,当系统需求变化时,可以方便地添加、删除或修改状态,无需修改大量代码。

扩展性强,方便添加新的状态。

代码的结构很清晰,每个状态类专门负责一种特定的行为。

对外隐藏细节,外部只需要关心当前的状态,不需要知道状态转换的细节。

状态模式的缺点:

如果状态很多,会导致类的数量增加。

有些状态处理场景会导致频繁创建和销毁状态对象,带来额外性能开销。

用类对象来表示状态,容易引起过度封装,导致代码结构复杂。

六,代码实战

Demo1:基于状态模式模拟的交通信号灯

cpp 复制代码
#include <iostream>
#include <thread>

class State {
public:
    virtual void handleState() = 0;
};

class GreenState: public State {
public:
    void handleState() override {
        std::cout << "Traffic Light: Green!" << std::endl;
    }
};

class RedState: public State {
public:
    void handleState() override {
        std::cout << "Traffic Light: Red!" << std::endl;
    }
};

class YellowState: public State {
public:
    void handleState() override {
        std::cout << "Traffic Light: Yellow!" << std::endl;
    }
};

class TrafficLight {
private:
    State* currentState;
public:
    TrafficLight(State* initialState){
        currentState = initialState;
    }
    void changeState(State* newState) {
        currentState = newState;
    }
    void operate() {
        currentState->handleState();
    }
};

int main() {
    GreenState greenState;
    RedState redState;
    YellowState yellowState;

    TrafficLight trafficLight(&greenState);
    trafficLight.operate();

    trafficLight.changeState(&yellowState);
    trafficLight.operate();
    trafficLight.changeState(&redState);
    trafficLight.operate();
    
    return 0;
}

运行结果:

bash 复制代码
Traffic Light: Green!
Traffic Light: Yellow!
Traffic Light: Red!

Demo2:基于状态模式模拟的网络管理

cpp 复制代码
#include <iostream>
class State;

class TCPConnection {
public:
    TCPConnection();
    void open();
    void close();
    void setState(State* newState);
private:
    State* currentState;
};

class State {
public:
    virtual void open(TCPConnection* connection) = 0;
    virtual void close(TCPConnection* connection) = 0;
};

class ReOpenState: public State{
public:
    void open(TCPConnection* connection) override {
        std::cout << "[State3]Network is already ReOpen" << std::endl;
    }
    void close(TCPConnection* connection) override {
        std::cout << "[State3]Closing Network" << std::endl;
    }
};

class ClosedState: public State{
public:
    void open(TCPConnection* connection) override {
        std::cout << "[State2]Opening Network" << std::endl;
        connection->setState(new ReOpenState());
    }
    void close(TCPConnection* connection) override {
        std::cout << "[State2]Network is already closed" << std::endl;
    }
};

class OpenState: public State{
public:
    void open(TCPConnection* connection) override {
        std::cout << "[State1]Network is already open" << std::endl;
    }
    void close(TCPConnection* connection) override {
        std::cout << "[State1]Closing Network" << std::endl;
        connection->setState(new ClosedState());
    }
};

TCPConnection::TCPConnection() : currentState(new OpenState()) {}
void TCPConnection::open() {
    currentState->open(this);
}
void TCPConnection::close() {
    currentState->close(this);
}
void TCPConnection::setState(State* newState) {
    currentState = newState;
}

int main() {
    TCPConnection tcpConnection;

    tcpConnection.open();
    tcpConnection.close();

    tcpConnection.open();
    tcpConnection.close();
    return 0;
}

运行结果:

bash 复制代码
[State1]Network is already open
[State1]Closing Network
[State2]Opening Network
[State3]Closing Network

七,参考阅读

https://www.javaskool.com/state-design-pattern/

https://www.geeksforgeeks.org/state-design-pattern/

https://softwarepatterns.com/cpp/state-software-pattern-cpp-example

https://www.codeproject.com/Articles/1087619/State-Machine-Design-in-Cplusplus-2

相关推荐
mit6.8248 分钟前
[Linux#49][UDP] 2w字详解 | socketaddr | 常用API | 实操:实现简易Udp传输
linux·网络·c++·笔记·后端
0点51 胜14 分钟前
[ffmpeg]音频格式转换
开发语言·c++·ffmpeg
我码玄黄16 分钟前
JS 的行为设计模式:策略、观察者与命令模式
javascript·设计模式·命令模式
水木流年追梦25 分钟前
【python因果推断库16】使用 PyMC 模型进行回归拐点设计
开发语言·python·回归
guigui_hello36 分钟前
VScode的简单使用
c++·ide·vscode·编辑器
不悔哥1 小时前
openwrt wsdd模块介绍
linux·c语言·网络·tcp/ip·智能路由器
只想摆烂@1 小时前
C# winfrom 如何多窗体优雅的回调方法
开发语言·c#
西猫雷婶1 小时前
python画图|中秋到了,尝试画个月亮(球体画法)
开发语言·python
星迹日1 小时前
C语言:结构体
c语言·开发语言·经验分享·笔记
会敲代码的小张1 小时前
设计模式-观察者模式
java·开发语言·后端·观察者模式·设计模式·代理模式