设计模式——状态模式

定义

状态模式(State Pattern)是一种行为设计模式。它允许一个对象在其内部状态改变时改变它的行为。对象看起来好像修改了它的类,从直观上看,就像是对象根据自身的状态来动态地切换行为方式。

结构组成

  • 环境(Context)类:
    环境类是拥有状态的对象,它维护一个具体状态类的实例,这个实例代表当前对象的状态。
    它定义了客户感兴趣的接口,并且负责具体状态的切换。例如,在一个简单的自动售货机系统中,自动售货机就是环境类,它有 "已投币""未投币" 等状态。
  • 抽象状态(State)类:
    这是所有具体状态类的基类,它定义了一个接口,用于封装与环境类的一个特定状态相关的行为。比如,在上述自动售货机例子中,抽象状态类可以定义像 "选择商品""退币" 等行为接口,这些接口的具体实现由具体状态类来完成。
  • 具体状态(Concrete State)类:
    具体状态类实现了抽象状态类中定义的接口。每个具体状态类对应环境类的一种状态,并且实现了在该状态下环境类应该具有的行为。以自动售货机为例,"未投币状态" 具体类会实现 "投币" 接口行为,当执行投币操作后,环境类(自动售货机)的状态可能会切换到 "已投币状态"。

工作原理

当环境类的内部状态发生改变时,它会调用当前状态对象的方法。这些方法的具体实现是在具体状态类中定义的。

例如,假设有一个文档编辑软件,文档可以处于 "只读" 和 "可编辑" 两种状态。文档对象(环境类)维护一个状态对象(具体是 "只读状态" 对象或者 "可编辑状态" 对象)。当用户试图修改文档内容时,如果文档处于 "可编辑" 状态,修改操作会正常进行;如果文档处于 "只读" 状态,修改操作会被禁止,可能会弹出一个提示框告知用户文档是只读的。这就是状态模式根据不同状态执行不同行为的体现。

代码示例

电灯

以下是一个简单的 C++ 状态模式示例,以电灯的开关状态为例。

  • 首先是抽象状态类:
cpp 复制代码
class LightState {
public:
    virtual void handle(Light* light) = 0;
};
  • 然后是具体状态类,比如 "开状态":
cpp 复制代码
class OnState : public LightState {
public:
    void handle(Light* light) override {
        std::cout << "灯已经是开着的。" << std::endl;
    }
};
  • 还有 "关状态":
cpp 复制代码
class OffState : public LightState {
public:
    void handle(Light* light) override {
        std::cout << "灯已经是关着的。" << std::endl;
    }
};

接着是环境类(电灯类):

cpp 复制代码
class Light {
private:
    LightState* state;
public:
    Light() {
        state = new OffState();
    }
    void setState(LightState* newState) {
        state = newState;
    }
    void request() {
        state->handle(this);
    }
};
  • 使用示例:
cpp 复制代码
int main() {
    Light light;
    light.request();
    LightState* on = new OnState();
    light.setState(on);
    light.request();
    return 0;
}

在这个示例中,Light类是环境类,LightState是抽象状态类,OnState和OffState是具体状态类。Light类的request方法根据当前的状态(state对象)来执行不同的行为,通过setState方法可以切换状态。

交通信号灯系统

  • 状态描述:
    交通信号灯有 "红灯""绿灯""黄灯" 三种状态,并且会按照一定的规则进行状态转换。
    环境(Context)类 - 交通信号灯类(TrafficLight):
    它维护交通信号灯的当前状态对象引用,并且有一个change()方法用于状态转换。
cpp 复制代码
class TrafficLight {
private:
    TrafficLightState* state;
public:
    TrafficLight() {
        state = new RedLightState();
    }
    void setState(TrafficLightState* newState) {
        state = newState;
    }
    void change() {
        state->change(this);
    }
};
  • 抽象状态(State)类 - 交通信号灯状态(TrafficLightState):
    定义了change()抽象方法来处理交通信号灯状态转换时的行为。
cpp 复制代码
class TrafficLightState {
public:
    virtual void change(TrafficLight* trafficLight) = 0;
};
  • 具体状态(Concrete State)类举例 - 红灯状态(RedLightState):
    当交通信号灯处于 "红灯" 状态时,车辆停止,行人可以通过。并且在一定时间后,状态会转换为 "绿灯"。
cpp 复制代码
class RedLightState : public TrafficLightState {
public:
    void change(TrafficLight* trafficLight) override {
        std::cout << "红灯亮,车辆停止,行人通过。" << std::endl;
        // 模拟时间延迟后转换状态
        std::this_thread::sleep_for(std::chrono::seconds(5));
        TrafficLightState* greenState = new GreenLightState();
        trafficLight->setState(greenState);
    }
};
  • 具体状态(Concrete State)类举例 - 绿灯状态(GreenLightState):
    当交通信号灯处于 "绿灯" 状态时,车辆可以通过,行人停止。一段时间后转换为 "黄灯"。
cpp 复制代码
class GreenLightState : public TrafficLightState {
public:
    void change(TrafficLight* trafficLight) override {
        std::cout << "绿灯亮,车辆通过,行人停止。" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
        TrafficLightState* yellowState = new YellowLightState();
        trafficLight->setState(yellowState);
    }
};
  • 具体状态(Concrete State)类举例 - 黄灯状态(YellowLightState):
    当交通信号灯处于 "黄灯" 状态时,车辆减速,准备停车。之后转换为 "红灯"。
cpp 复制代码
class YellowLightState : public TrafficLightState {
public:
    void change(TrafficLight* trafficLight) override {
        std::cout << "黄灯亮,车辆减速。" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
        TrafficLightState* redState = new RedLightState();
        trafficLight->setState(redState);
    }
};
  • 状态转换示例:
    模拟交通信号灯的状态转换过程。
cpp 复制代码
int main() {
    TrafficLight trafficLight;
    for (int i = 0; i < 10; ++i) {
        trafficLight.change();
    }
    return 0;
}

优点

  • 提高可维护性:
    将不同状态下的行为封装到不同的状态类中,使得代码结构更加清晰。当需要修改某个状态下的行为时,只需要修改对应的具体状态类,而不会影响到其他状态的代码。例如,在一个复杂的游戏角色状态系统中,角色有 "行走""攻击""防御" 等状态,使用状态模式后,如果要修改 "攻击" 状态下的行为逻辑,只需要在 "攻击状态" 具体类中修改即可。
  • 符合开闭原则:
    可以很容易地增加新的状态类来扩展系统功能。比如,在上述游戏角色状态系统中,如果要添加一个 "施法" 状态,只需要创建一个新的 "施法状态" 具体类,实现相应的行为接口,然后在环境类(游戏角色类)中进行适当的状态切换处理即可,不需要修改原有的状态类代码。

缺点

  • 增加类的数量:
    每一个状态都需要一个具体状态类来实现,会导致系统中类的数量增加。在一个状态较多的复杂系统中,可能会产生大量的状态类,使得代码的管理和理解变得困难。例如,一个具有几十种不同状态的工业控制系统,如果使用状态模式,会有大量的状态类文件,增加了代码的复杂性。
  • 系统结构复杂:
    状态模式的实现涉及到环境类、抽象状态类和多个具体状态类之间的交互,对于不熟悉设计模式的开发人员来说,理解和实现起来可能会有一定的难度。特别是在状态转换逻辑比较复杂的情况下,可能会出现状态转换错误等问题。
相关推荐
ke_wu3 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
小马爱打代码3 小时前
设计模式详解(建造者模式)
java·设计模式·建造者模式
小王爱吃月亮糖3 小时前
C++的23种设计模式
开发语言·c++·qt·算法·设计模式·ecmascript
_im.m.z4 小时前
【设计模式学习笔记】1. 设计模式概述
笔记·学习·设计模式
转转技术团队7 小时前
【述职黑话】ToB交易业务解决方案之状态机
java·状态模式
bandaoyu10 小时前
【设计模式】装饰器模式(Decorator Pattern)
设计模式·装饰器模式
lijiachang03071812 小时前
设计模式(一):单例模式
c++·笔记·学习·程序人生·单例模式·设计模式·大学生
抓哇FullStack-Junior12 小时前
设计模式——适配器模式
java·设计模式·适配器模式
silver68714 小时前
桥接模式详解
设计模式
思忖小下15 小时前
梳理你的思路(从OOP到架构设计)_设计模式Observer模式
观察者模式·设计模式·eit