设计模式:状态模式(State Pattern)
文章目录
- 一、状态模式的介绍
- 二、实例分析
- 三、示例代码
一、状态模式的介绍
状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变它的行为,看起来就像是对象改变了它的类一样。
通俗理解:
- 一个对象的行为依赖于它的状态(例如:登录状态 / 未登录状态)。
- 当状态发生改变时,对象表现出的行为也随之发生变化。
- 使用状态模式可以避免在对象中堆积大量的 if-else 或 switch 语句。
主要包含以下几个角色:
- Context(环境类):维护一个当前状态对象的引用。提供接口给客户端调用,并在内部把请求委托给当前的状态对象来处理。
- State(抽象状态类/接口):定义状态对象的共同接口,不同状态下的行为都需要实现这个接口。
- ConcreteState(具体状态类):实现抽象状态定义的行为。每个具体状态类实现对象在该状态下的特定行为。
状态模式结构:
二、实例分析
问题:
状态模式与有限状态机 的概念紧密相关。
其主要思想是程序在任意时刻仅可处于几种有限的状态中。 在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。 不过, 根据当前状态, 程序可能会切换到另外一种状态, 也可能会保持当前状态不变。 这些数量有限且预先定义的状态切换规则被称为转移。
你还可将该方法应用在对象上。 假如你有一个文档Document类。 文档可能会处于草稿Draft 、 审阅中Moderation和已发布Published三种状态中的一种。 文档的publish发布方法在不同状态下的行为略有不同:
- 处于草稿状态时, 它会将文档转移到审阅中状态。
- 处于审阅中状态时, 如果当前用户是管理员, 它会公开发布文档。
- 处于已发布状态时, 它不会进行任何操作。
状态机通常由众多条件运算符 (if或switch)实现, 可根据对象的当前状态选择相应的行为。 “状态” 通常只是对象中的一组成员变量值。 即使你之前从未听说过有限状态机, 你也很可能已经实现过状态模式。
当我们逐步在文档类中添加更多状态和依赖于状态的行为后, 基于条件语句的状态机就会暴露其最大的弱点。 为了能根据当前状态选择完成相应行为的方法, 绝大部分方法中会包含复杂的条件语句。 修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句, 导致代码的维护工作非常艰难。
这个问题会随着项目进行变得越发严重。 我们很难在设计阶段预测到所有可能的状态和转换。 随着时间推移, 最初仅包含有限条件语句的简洁状态机可能会变成臃肿的一团乱麻。
解决方案:
状态模式建议为对象的所有可能状态新建一个类, 然后将所有状态的对应行为抽取到这些类中。
原始对象被称为上下文 (context), 它并不会自行实现所有行为, 而是会保存一个指向表示当前状态的状态对象的引用, 且将所有与状态相关的工作委派给该对象。
如需将上下文转换为另外一种状态, 则需将当前活动的状态对象替换为另外一个代表新状态的对象。 采用这种方式是有前提的: 所有状态类都必须遵循同样的接口, 而且上下文必须仅通过接口与这些对象进行交互。
这个结构可能看上去与策略模式相似, 但有一个关键性的不同——在状态模式中, 特定状态知道其他所有状态的存在, 且能触发从一个状态到另一个状态的转换; 策略则几乎完全不知道其他策略的存在。
三、示例代码
示例一:电梯状态
#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;
}
示例二:登录状态(未登录、已登录)
#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();
}
示例三:订单状态(待支付、已支付、已发货、已完成)
#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;
}
示例四:文档审批/发布流程
#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;
}
示例五:智能手机按键的状态机
需求分析:
- 解锁状态:按下按键 → 执行各种功能(比如打开相机、拨号等)。
- 锁定状态:按下按键 → 解锁屏幕。
- 电量不足状态:按下按键 → 显示充电页面。
#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;
}