设计模式:状态模式(State Pattern)

文章目录

一、状态模式的介绍

状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,看起来就像是对象改变了它的类一样。

通俗理解:

  • 一个对象的行为依赖于它的状态(例如:登录状态 / 未登录状态)。
  • 当状态发生改变时,对象表现出的行为也随之发生变化。
  • 使用状态模式可以避免在对象中堆积大量的 if-else 或 switch 语句。

主要包含以下几个角色:

  1. Context(环境类):维护一个当前状态对象的引用。提供接口给客户端调用,并在内部把请求委托给当前的状态对象来处理。
  2. State(抽象状态类/接口):定义状态对象的共同接口,不同状态下的行为都需要实现这个接口。
  3. ConcreteState(具体状态类):实现抽象状态定义的行为。每个具体状态类实现对象在该状态下的特定行为。

状态模式结构:

二、实例分析

问题:

状态模式与有限状态机 的概念紧密相关。

其主要思想是程序在任意时刻仅可处于几种有限的状态中。 在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。 不过, 根据当前状态, 程序可能会切换到另外一种状态, 也可能会保持当前状态不变。 这些数量有限且预先定义的状态切换规则被称为转移。

你还可将该方法应用在对象上。 假如你有一个文档Document类。 文档可能会处于草稿Draft 、 ​ 审阅中Moderation和已发布Published三种状态中的一种。 文档的publish发布方法在不同状态下的行为略有不同:

  • 处于草稿状态时, 它会将文档转移到审阅中状态。
  • 处于审阅中状态时, 如果当前用户是管理员, 它会公开发布文档。
  • 处于已发布状态时, 它不会进行任何操作。

状态机通常由众多条件运算符 (if或switch)实现, 可根据对象的当前状态选择相应的行为。 ​ "状态" 通常只是对象中的一组成员变量值。 即使你之前从未听说过有限状态机, 你也很可能已经实现过状态模式。

当我们逐步在文档类中添加更多状态和依赖于状态的行为后, 基于条件语句的状态机就会暴露其最大的弱点。 为了能根据当前状态选择完成相应行为的方法, 绝大部分方法中会包含复杂的条件语句。 修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句, 导致代码的维护工作非常艰难。

这个问题会随着项目进行变得越发严重。 我们很难在设计阶段预测到所有可能的状态和转换。 随着时间推移, 最初仅包含有限条件语句的简洁状态机可能会变成臃肿的一团乱麻。

解决方案:

状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。

原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象。

如需将上下文转换为另外一种状态, 则需将当前活动的状态对象替换为另外一个代表新状态的对象。 采用这种方式是有前提的: 所有状态类都必须遵循同样的接口, 而且上下文必须仅通过接口与这些对象进行交互。

这个结构可能看上去与策略模式相似, 但有一个关键性的不同------在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换; 策略则几乎完全不知道其他策略的存在。

三、示例代码

示例一:电梯状态

cpp 复制代码
#include <iostream>
using namespace std;

// 抽象状态类
class State {
public:
    virtual void handle() = 0;
    virtual ~State() = default;
};

// 具体状态类 - 开门
class OpenState : public State {
public:
    void handle() override {
        cout << "电梯门打开..." << endl;
    }
};

// 具体状态类 - 关门
class CloseState : public State {
public:
    void handle() override {
        cout << "电梯门关闭..." << endl;
    }
};

// 环境类
class Context {
private:
    State* state;
public:
    Context(State* s) : state(s) {}
    void setState(State* s) {
        state = s;
    }
    void request() {
        state->handle();
    }
};

int main() {
    // 初始状态:关门
    State* close = new CloseState();
    Context* ctx = new Context(close);
    ctx->request();

    // 切换到开门
    State* open = new OpenState();
    ctx->setState(open);
    ctx->request();

    delete close;
    delete open;
    delete ctx;
    return 0;
}

示例二:登录状态(未登录、已登录)

cpp 复制代码
#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include <QString>

// ================== 抽象状态类 ==================
class State {
public:
    virtual ~State() {}
    virtual QString text() const = 0;   // 按钮文字
    virtual State* nextState() = 0;     // 切换到下一个状态
};

// ================== 具体状态类 ==================

// 未登录
class UnLoginState : public State {
public:
    QString text() const override {
        return "未登录";
    }
    State* nextState() override;
};

// 已登录
class LoginState : public State {
public:
    QString text() const override {
        return "已登录";
    }
    State* nextState() override;
};

// 登录过期
class ExpiredState : public State {
public:
    QString text() const override {
        return "登录过期";
    }
    State* nextState() override;
};

// ================== 状态切换实现 ==================
State* UnLoginState::nextState() {
    return new LoginState();
}

State* LoginState::nextState() {
    return new ExpiredState();
}

State* ExpiredState::nextState() {
    return new UnLoginState();
}

// ================== 主窗口类 ==================
class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr) : QMainWindow(parent), m_state(new UnLoginState()) {
        m_button = new QPushButton(m_state->text(), this);
        m_button->setFixedSize(120, 40);
        m_button->move(50, 50);

        connect(m_button, &QPushButton::clicked, this, &MainWindow::onButtonClicked);
    }

    ~MainWindow() {
        delete m_state;
    }

private slots:
    void onButtonClicked() {
        State* newState = m_state->nextState();
        delete m_state;
        m_state = newState;
        m_button->setText(m_state->text());
    }

private:
    QPushButton* m_button;
    State* m_state;
};

// ================== main 函数 ==================
int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("状态模式示例");
    w.resize(220, 150);
    w.show();
    return a.exec();
}

示例三:订单状态(待支付、已支付、已发货、已完成)

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 抽象状态类
class State {
public:
    virtual ~State() {}
    virtual string name() const = 0;       // 状态名字
    virtual State* nextState() = 0;        // 切换到下一个状态
};

// 待支付
class PendingState : public State {
public:
    string name() const override { return "待支付"; }
    State* nextState() override; // 声明
};

// 已支付
class PaidState : public State {
public:
    string name() const override { return "已支付"; }
    State* nextState() override;
};

// 已发货
class ShippedState : public State {
public:
    string name() const override { return "已发货"; }
    State* nextState() override;
};

// 已完成
class CompletedState : public State {
public:
    string name() const override { return "已完成"; }
    State* nextState() override { 
        cout << "订单已完成,无法再改变状态。" << endl;
        return this; // 保持在已完成状态
    }
};

// ========== 状态切换实现 ==========
State* PendingState::nextState() { return new PaidState(); }
State* PaidState::nextState()    { return new ShippedState(); }
State* ShippedState::nextState() { return new CompletedState(); }

// ========== 环境类(订单) ==========
class Order {
private:
    State* state;
public:
    Order() : state(new PendingState()) {}
    ~Order() { delete state; }

    void advance() {
        State* newState = state->nextState();
        if (newState != state) { // 不是停留状态,说明切换了
            delete state;
            state = newState;
        }
        cout << "当前订单状态:" << state->name() << endl;
    }

    string status() const { return state->name(); }
};

// ========== 测试 ==========
int main() {
    Order order;
    cout << "初始状态:" << order.status() << endl;

    order.advance(); // 待支付 -> 已支付
    order.advance(); // 已支付 -> 已发货
    order.advance(); // 已发货 -> 已完成
    order.advance(); // 已完成 -> 仍是已完成

    return 0;
}

示例四:文档审批/发布流程

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 用户角色
enum class Role {
    User,
    Admin
};

// 抽象状态类
class State {
public:
    virtual ~State() {}
    virtual string name() const = 0;
    virtual State* publish(Role role) = 0;  // 状态转换逻辑
};

// ============ Draft 草稿 ============
class DraftState : public State {
public:
    string name() const override { return "草稿"; }
    State* publish(Role role) override;
};

// ============ Moderation 审阅中 ============
class ModerationState : public State {
public:
    string name() const override { return "审阅中"; }
    State* publish(Role role) override;
};

// ============ Published 已发布 ============
class PublishedState : public State {
public:
    string name() const override { return "已发布"; }
    State* publish(Role role) override {
        cout << "文档已发布,无法进行操作。" << endl;
        return this; // 保持当前状态
    }
};

// ============ 状态切换实现 ============
State* DraftState::publish(Role role) {
    if (role == Role::User) {
        cout << "用户发表 → 转入审阅中" << endl;
        return new ModerationState();
    } else {
        cout << "管理员直接发表 → 转入已发布" << endl;
        return new PublishedState();
    }
}

State* ModerationState::publish(Role role) {
    if (role == Role::Admin) {
        cout << "管理员批准 → 转入已发布" << endl;
        return new PublishedState();
    } else {
        cout << "普通用户无法操作,保持在审阅中" << endl;
        return this;
    }
}

// ============ 文档环境类 ============
class Document {
private:
    State* state;
public:
    Document() : state(new DraftState()) {}
    ~Document() { delete state; }

    void publish(Role role) {
        State* newState = state->publish(role);
        if (newState != state) {
            delete state;
            state = newState;
        }
        cout << "当前状态:" << state->name() << endl;
    }
};

// ============ 测试 ============
int main() {
    Document doc;
    cout << "初始状态:草稿" << endl;

    doc.publish(Role::User);   // 用户发表 -> 审阅中
    doc.publish(Role::User);   // 普通用户无权发布 -> 仍然审阅中
    doc.publish(Role::Admin);  // 管理员批准 -> 已发布
    doc.publish(Role::Admin);  // 已发布 -> 无操作

    return 0;
}

示例五:智能手机按键的状态机

需求分析:

  • 解锁状态:按下按键 → 执行各种功能(比如打开相机、拨号等)。
  • 锁定状态:按下按键 → 解锁屏幕。
  • 电量不足状态:按下按键 → 显示充电页面。
cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 抽象状态类
class State {
public:
    virtual ~State() {}
    virtual void onPress() = 0;    // 按下按键行为
};

// ========== 具体状态类 ==========

// 解锁状态
class UnlockedState : public State {
public:
    void onPress() override {
        cout << "已解锁:执行按键功能(如打开相机、拨号)" << endl;
    }
};

// 锁定状态
class LockedState : public State {
public:
    void onPress() override {
        cout << "处于锁定:按键 → 解锁屏幕" << endl;
    }
};

// 电量不足状态
class LowBatteryState : public State {
public:
    void onPress() override {
        cout << "电量不足:按键 → 显示充电页面" << endl;
    }
};

// ========== 环境类(智能手机) ==========
class Smartphone {
private:
    State* state;
public:
    Smartphone(State* initState) : state(initState) {}
    ~Smartphone() { delete state; }

    void setState(State* newState) {
        delete state;
        state = newState;
    }

    void pressButton() {
        state->onPress();
    }
};

// ========== 测试 ==========
int main() {
    // 初始状态:锁定
    Smartphone phone(new LockedState());

    phone.pressButton();  // 锁定 → 解锁屏幕

    phone.setState(new UnlockedState());
    phone.pressButton();  // 解锁 → 执行功能

    phone.setState(new LowBatteryState());
    phone.pressButton();  // 电量不足 → 显示充电页面

    return 0;
}
相关推荐
努力也学不会java6 小时前
【设计模式】 原型模式
java·设计模式·原型模式
TechNomad8 小时前
设计模式:模板方法模式(Template Method Pattern)
设计模式·模板方法模式
leo030810 小时前
7种流行Prompt设计模式详解:适用场景与最佳实践
设计模式·prompt
ytadpole13 小时前
揭秘设计模式:工厂模式的五级进化之路
java·设计模式
烛阴13 小时前
【TS 设计模式完全指南】用工厂方法模式打造你的“对象生产线”
javascript·设计模式·typescript
_请输入用户名16 小时前
EventEmitter 是广播,Tapable 是流水线:聊聊它们的本质区别
前端·设计模式
Buling_017 小时前
游戏中的设计模式——第一篇 设计模式简介
游戏·设计模式
小蜗牛在漫步17 小时前
设计模式六大原则2-里氏替换原则
设计模式·里氏替换原则
小蜗牛在漫步19 小时前
23种设计模式-Proxy模式
设计模式·代理模式