【C++设计模式】第十九篇:状态模式(State)
注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。
对象行为的动态切换
1. 模式定义与用途
核心思想
- 状态模式:允许对象在其内部状态改变时改变行为,将状态逻辑封装为独立的类,使状态转换显式化。
- 关键用途:
1.替代条件分支:消除复杂的if-else
或switch-case
结构。
2.简化状态迁移:每个状态类仅关注自身行为及转换条件。
3.动态切换行为:运行时通过切换状态对象改变对象行为。
经典场景
- 电梯控制系统(停止、运行、故障状态)。
- 订单生命周期(待支付、已发货、已完成)。
- 游戏角色状态(移动、攻击、防御)。
2. 模式结构解析
UML类图
+---------------------+ +---------------------+
| Context | | State |
+---------------------+ +---------------------+
| - state: State |<>------->| + handle(): void |
+---------------------+ +---------------------+
| + setState(s: State)| ^
| + request() | |
+---------------------+ +-------+-------+
| |
+----------------+ +----------------+
| ConcreteStateA | | ConcreteStateB |
+----------------+ +----------------+
| + handle() | | + handle() |
+----------------+ +----------------+
角色说明
Context
:上下文,维护当前状态对象,定义客户端接口(如request()
)。-
State
:抽象状态接口,定义状态行为(如handle()
)。 ConcreteState
:具体状态类,实现特定状态的行为与转换逻辑。
3. 现代C++实现示例
场景:电梯状态控制
步骤1:定义状态接口与上下文
#include <iostream>
#include <memory>
class Elevator; // 前向声明
// 抽象状态
class ElevatorState {
public:
virtual ~ElevatorState() = default;
virtual void openDoors(Elevator* elevator) = 0;
virtual void closeDoors(Elevator* elevator) = 0;
virtual void move(Elevator* elevator) = 0;
};
// 上下文:电梯
class Elevator {
public:
Elevator();
void setState(std::unique_ptr<ElevatorState> state);
// 客户端接口
void pressOpenButton() { state_->openDoors(this); }
void pressCloseButton() { state_->closeDoors(this); }
void pressMoveButton() { state_->move(this); }
private:
std::unique_ptr<ElevatorState> state_;
};
步骤2:实现具体状态类
// 停止状态
class StoppedState : public ElevatorState {
public:
void openDoors(Elevator* elevator) override {
std::cout << "门已打开\n";
elevator->setState(std::make_unique<OpenState>());
}
void closeDoors(Elevator* elevator) override {
std::cout << "门已关闭\n";
}
void move(Elevator* elevator) override {
std::cout << "电梯开始移动\n";
elevator->setState(std::make_unique<MovingState>());
}
};
// 开门状态
class OpenState : public ElevatorState {
public:
void openDoors(Elevator*) override {
std::cout << "错误:门已经是开着的\n";
}
void closeDoors(Elevator* elevator) override {
std::cout << "门已关闭\n";
elevator->setState(std::make_unique<StoppedState>());
}
void move(Elevator*) override {
std::cout << "错误:门未关闭,无法移动\n";
}
};
// 移动状态
class MovingState : public ElevatorState {
public:
void openDoors(Elevator*) override {
std::cout << "错误:移动中不能开门\n";
}
void closeDoors(Elevator*) override {
std::cout << "错误:门已经是关着的\n";
}
void move(Elevator* elevator) override {
std::cout << "电梯停止移动\n";
elevator->setState(std::make_unique<StoppedState>());
}
};
// 上下文初始化
Elevator::Elevator() : state_(std::make_unique<StoppedState>()) {}
void Elevator::setState(std::unique_ptr<ElevatorState> state) {
state_ = std::move(state);
}
步骤3:客户端代码
int main() {
Elevator elevator;
elevator.pressMoveButton(); // 移动
elevator.pressOpenButton(); // 错误:移动中不能开门
elevator.pressMoveButton(); // 停止移动
elevator.pressOpenButton(); // 开门
elevator.pressCloseButton(); // 关门
}
/* 输出:
电梯开始移动
错误:移动中不能开门
电梯停止移动
门已打开
门已关闭
*/
4. 应用场景示例
场景1:订单状态流转
class Order;
class OrderState {
public:
virtual void pay(Order* order) = 0;
virtual void ship(Order* order) = 0;
virtual void complete(Order* order) = 0;
};
class Order {
public:
Order();
void setState(std::unique_ptr<OrderState> state);
void processPayment() { state_->pay(this); }
// ...其他操作
};
// 具体状态:待支付
class UnpaidState : public OrderState {
void pay(Order* order) override {
std::cout << "订单已支付\n";
order->setState(std::make_unique<PaidState>());
}
// ...其他方法抛出异常或忽略
};
场景2:游戏角色状态切换
class Character;
class CharacterState {
public:
virtual void move(Character* character) = 0;
virtual void attack(Character* character) = 0;
};
class IdleState : public CharacterState {
void move(Character* c) override {
c->setState(std::make_unique<MovingState>());
}
void attack(Character*) override { /* 待机状态无法攻击 */ }
};
5. 优缺点分析
优点 | 缺点 |
---|---|
消除复杂条件分支,提升可读性 | 增加类的数量(每个状态对应一个类) |
状态转换逻辑集中管理 | 状态较多时类间关系复杂化 |
符合开闭原则(新增状态无需修改现有代码) | 上下文需持有所有状态类的引用 |
6. 调试与优化策略
调试技巧(VS2022)
1. 跟踪状态转换:
- 在setState()方法内设置断点,观察状态切换是否符合预期。
2. 状态日志记录:
void Order::setState(std::unique_ptr<OrderState> state) {
std::cout << "状态切换至: " << typeid(*state).name() << "\n";
state_ = std::move(state);
}
性能优化
1. 状态对象复用:
- 使用对象池共享状态实例(若状态无内部数据)。
class StatePool {
public:
static StoppedState& getStoppedState() {
static StoppedState instance;
return instance;
}
};
2. 避免频繁状态切换:
- 合并相邻状态或延迟状态切换(如批量处理事件)。