在c++开发中,当对象的行为依赖于其内部状态,并且状态会在运行时发生变化时,如果直接使用大量的if-else或者switch-case语句判断状态再执行对应逻辑,会导致代码臃肿、扩展性差。状态模式通过将每个状态的行为封装到独立的类中,让对象在状态改变时切换对应的状态类,从而自然改变自身行为,是解决这类问题的优选方案。

状态模式的核心结构
状态模式主要包含以下几个角色:
- 环境类(Context):维护当前状态对象的实例,对外提供操作接口,状态变化时会切换持有的状态对象。
- 抽象状态类(State):定义所有状态共同的行为接口,所有具体状态类都需要实现这个接口。
- 具体状态类(ConcreteState):实现抽象状态类的接口,每个类对应一种具体状态,实现该状态下的具体行为逻辑。
c++实现状态模式示例
我们以一个简单的电梯运行为例,电梯有停止、运行、故障三种状态,不同状态下对开门、关门、运行指令的响应不同,下面用状态模式实现这个场景。
1. 定义抽象状态类
首先定义电梯状态的抽象接口,包含开门、关门、运行三个操作:
// 前置声明环境类
class Elevator;
// 抽象状态类
class ElevatorState {
public:
virtual ~ElevatorState() = default;
// 开门操作
virtual void openDoor(Elevator* elevator) = 0;
// 关门操作
virtual void closeDoor(Elevator* elevator) = 0;
// 运行操作
virtual void run(Elevator* elevator) = 0;
};
2. 实现具体状态类
分别实现停止、运行、故障三个具体状态类:
#include <iostream>
#include <string>
// 停止状态
class StopState : public ElevatorState {
public:
void openDoor(Elevator* elevator) override;
void closeDoor(Elevator* elevator) override;
void run(Elevator* elevator) override;
};
// 运行状态
class RunState : public ElevatorState {
public:
void openDoor(Elevator* elevator) override;
void closeDoor(Elevator* elevator) override;
void run(Elevator* elevator) override;
};
// 故障状态
class FaultState : public ElevatorState {
public:
void openDoor(Elevator* elevator) override;
void closeDoor(Elevator* elevator) override;
void run(Elevator* elevator) override;
};
// 停止状态的行为实现
void StopState::openDoor(Elevator* elevator) {
std::cout << "电梯已停止,开门成功" << std::endl;
}
void StopState::closeDoor(Elevator* elevator) {
std::cout << "电梯已停止,关门成功" << std::endl;
}
void StopState::run(Elevator* elevator) {
std::cout << "电梯停止状态,开始运行" << std::endl;
// 切换到运行状态
// 实际实现中会在Elevator类中提供设置状态的方法
}
// 运行状态的行为实现
void RunState::openDoor(Elevator* elevator) {
std::cout << "电梯正在运行,禁止开门" << std::endl;
}
void RunState::closeDoor(Elevator* elevator) {
std::cout << "电梯正在运行,门已关闭" << std::endl;
}
void RunState::run(Elevator* elevator) {
std::cout << "电梯已经在运行中" << std::endl;
}
// 故障状态的行为实现
void FaultState::openDoor(Elevator* elevator) {
std::cout << "电梯故障,无法开门" << std::endl;
}
void FaultState::closeDoor(Elevator* elevator) {
std::cout << "电梯故障,无法关门" << std::endl;
}
void FaultState::run(Elevator* elevator) {
std::cout << "电梯故障,无法运行" << std::endl;
}
3. 实现环境类
环境类维护当前状态,提供操作接口并委托给当前状态对象执行:
class Elevator {
private:
ElevatorState* currentState;
// 具体状态实例,实际开发中可以用智能指针管理生命周期
StopState* stopState;
RunState* runState;
FaultState* faultState;
public:
Elevator() {
stopState = new StopState();
runState = new RunState();
faultState = new FaultState();
// 初始状态为停止状态
currentState = stopState;
}
~Elevator() {
delete stopState;
delete runState;
delete faultState;
}
// 设置当前状态
void setState(ElevatorState* state) {
currentState = state;
}
// 获取停止状态实例
ElevatorState* getStopState() {
return stopState;
}
// 获取运行状态实例
ElevatorState* getRunState() {
return runState;
}
// 获取故障状态实例
ElevatorState* getFaultState() {
return faultState;
}
// 开门操作
void openDoor() {
currentState->openDoor(this);
}
// 关门操作
void closeDoor() {
currentState->closeDoor(this);
}
// 运行操作
void run() {
currentState->run(this);
}
};
4. 完善状态切换逻辑
补充之前具体状态类中状态切换的实现,让状态可以正确流转:
// 补充StopState的run方法实现
void StopState::run(Elevator* elevator) {
std::cout << "电梯停止状态,开始运行" << std::endl;
elevator->setState(elevator->getRunState());
}
// 补充RunState的状态切换示例,比如故障后切换状态
// 可以在RunState中增加故障触发的方法,这里简化为运行状态下触发故障
void RunState::openDoor(Elevator* elevator) {
std::cout << "电梯正在运行,禁止开门,触发故障保护" << std::endl;
elevator->setState(elevator->getFaultState());
}
5. 测试代码
编写测试代码验证状态模式的效果:
int main() {
Elevator elevator;
std::cout << "=== 初始停止状态操作 ===" << std::endl;
elevator.openDoor();
elevator.closeDoor();
elevator.run();
std::cout << "n=== 运行状态操作 ===" << std::endl;
elevator.openDoor(); // 运行状态下开门触发故障
elevator.run();
std::cout << "n=== 故障状态操作 ===" << std::endl;
elevator.openDoor();
elevator.closeDoor();
return 0;
}
状态模式的优势与适用场景
状态模式的优势非常明显:首先它消除了大量的条件判断语句,让代码逻辑更清晰;其次新增状态时只需要新增具体状态类,不需要修改原有代码,符合开闭原则;另外每个状态的行为独立封装,职责单一,便于维护和测试。
它适合以下场景:对象的行为依赖其状态,并且状态会在运行时频繁变化;代码中包含大量与对象状态相关的条件判断语句,导致代码难以维护和扩展。比如游戏角色的状态切换、订单流程的状态流转、设备的工作状态管理等场景都可以使用状态模式实现。
注意事项
使用状态模式时也需要注意,如果对象的状态很少,且状态变化逻辑简单,使用状态模式会增加类的数量,反而让代码变复杂。另外状态对象的生命周期管理需要注意,避免内存泄漏,实际开发中可以使用std::unique_ptr等智能指针管理状态对象的生命周期。