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

相关推荐
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师2 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java4 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
hikktn4 小时前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust
青花瓷4 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
睡觉谁叫~~~4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust