状态模式(State):让对象按状态自动改行为
更新时间:2026-08-25。本文按"先看不用模式的困境 → 再用模式重构 → 类图对比分析"的结构,论述状态模式。可运行代码:demos/design-patterns/state。
一、问题场景:对象行为随内部状态改变
TCP 连接(LISTEN/ESTABLISHED/CLOSE_WAIT…)、订单(待支付/已支付/已发货/已完成)、播放器(播放/暂停/停止)。同一操作在不同状态下行为不同,状态还会迁移。
二、不使用模式:if/else 状态机
cpp
void Order::pay() {
if (status_ == UNPAID) { status_ = PAID; /* 支付 */ }
else if (status_ == PAID) { /* 重复支付报错 */ }
else if (status_ == SHIPPED) { /* 已发货不能支付 */ }
}
void Order::ship() {
if (status_ == PAID) { status_ = SHIPPED; }
else if (status_ == UNPAID) { /* 未支付不能发货 */ }
}
// 每个操作都有一整套状态判断;状态 + 操作 = N×M 组合| 缺陷 | 说明 |
|---|---|
| 组合爆炸 | 状态数 × 操作数 的 if/else 交织 |
| 违反 SRP/OCP | 加状态 = 所有方法都加分支 |
| 迁移逻辑散落 | 每个方法自己改 status_,迁移规则不集中 |
| 状态判断重复 | 同类判断在不同方法里反复出现 |
不使用模式的类图

三、使用模式:每个状态一个类,迁移由状态类驱动
状态模式把每个状态封装成独立类(实现统一状态接口),上下文持有当前状态对象;所有操作委托给当前状态,状态类自己决定"迁移到哪个下一状态"(返回/切换新状态对象)。
cpp
// order_state.h
class Order; // 前向声明
class OrderState { // 状态抽象
public:
virtual void pay(Order& ctx) {}
virtual void ship(Order& ctx) {}
virtual void complete(Order& ctx) {}
virtual ~OrderState() = default;
};
class UnpaidState : public OrderState {
public:
void pay(Order& ctx) override { ctx.setState(PAID()); std::cout << "已支付\n"; }
void ship(Order& ctx) override { std::cout << "未支付不能发货\n"; }
};
class PaidState : public OrderState {
public:
void pay(Order& ctx) override { std::cout << "重复支付\n"; }
void ship(Order& ctx) override { ctx.setState(SHIPPED()); }
};
class ShippedState : public OrderState {
public:
void complete(Order& ctx) override { ctx.setState(DONE()); }
};
class Order { // 上下文
public:
Order() : state_(std::make_unique<UnpaidState>()) {}
void setState(std::unique_ptr<OrderState> s) { state_ = std::move(s); }
void pay() { state_->pay(*this); } // 全部委托当前状态
void ship() { state_->ship(*this); }
private:
std::unique_ptr<OrderState> state_;
};
// 使用:Order o; o.pay(); o.ship(); o.complete(); —— 行为自动跟随状态使用模式的类图

四、类图对比与分析
| 维度 | 不使用(if/else 状态机) | 使用状态模式 |
|---|---|---|
| 加新状态 | 所有方法加分支 | 新增状态类 + 定义迁移 |
| 迁移规则 | 散落各方法 | 集中在状态类(谁到谁,清楚) |
| 非法操作 | 每个方法重复判断 | 各状态类自己拒绝 |
| 代码结构 | 状态×操作交织 | 按状态分文件,每个类 SRP |
| 对象数量 | 一个对象 | 上下文 + 状态对象(可复用/缓存) |
分析结论:状态模式把"N×M 的 if/else 网格"摊平成"M 个状态类 × 各自的操作实现",迁移规则变成显式的"状态 A → 状态 B"连接。代价是类数量多(每状态一个类)、状态切换有对象创建开销(可用预建/缓存优化)。当状态数 ≥ 3 且状态间迁移复杂时收益显著。
五、适用边界
| 适用 | 不适用 |
|---|---|
| 状态数多、迁移规则复杂 | 状态只有 2~3 个且操作简单(if 即可) |
| 状态迁移频繁、规则集中管理 | 状态不可变、行为与状态无关 |
| 同一操作多状态行为差异大 | 极热路径(每操作一次状态对象切换) |
与策略模式的区别:策略是"外部喂算法"(对象自身不迁移);状态是"内部驱动迁移"(对象行为自己跟着状态走)。与责任链的区别:责任链是"请求沿链找处理者";状态是"对象自身状态决定行为"。
一句话总结
状态模式把每个状态封装成类,上下文委托给当前状态对象、迁移由状态类自己驱动——if/else 状态机摊平为按状态分组的类,加状态不再改旧方法;当状态数多、迁移复杂时它让状态机可读可扩展,代价是类数量与状态切换开销。