C++设计模式:状态模式(自动售货机)
什么是状态模式?
状态模式是一种行为型设计模式,它允许一个对象在其内部状态发生改变时,动态改变其行为。通过将状态相关的逻辑封装到独立的类中,状态模式能够将状态管理与行为解耦,从而让系统更加灵活和可维护。
通俗理解
想象一个自动售货机,它有以下几种状态:
- 无硬币状态:等待用户投入硬币。
- 有硬币状态:用户可以选择商品。
- 售货状态:用户选择商品后,售货机正在出货。
- 缺货状态:商品已售罄。
售货机的行为完全依赖于当前的状态,比如:
- 在无硬币状态下,用户无法选择商品。
- 在有硬币状态下,用户可以选择商品。
- 在售货状态下,售货机执行出货操作。
状态模式的核心就是:将状态逻辑抽象为独立的状态类,并通过上下文类(如售货机)动态切换状态对象,进而改变对象的行为。
状态模式的特点
优点
-
清晰封装状态逻辑:
- 每种状态的逻辑被集中到对应的状态类中,逻辑清晰且易于管理。
-
动态切换行为:
- 对象的行为可以通过切换状态动态改变,无需修改上下文类的代码。
-
扩展性强:
- 新增状态只需添加新的状态类,不影响现有代码,符合开闭原则。
缺点
-
类的数量增加:
- 每种状态都需要一个单独的类,可能导致类数量较多。
-
状态切换逻辑分散:
- 状态之间的切换逻辑分布在多个状态类中,增加了维护的复杂性。
状态模式的结构
UML类图
+---------------------+
| Context | // 上下文类,管理当前状态
+---------------------+
| - state: State |
| + setState(state) |
| + request() |
+---------------------+
^
|
+---------------------+
| State | // 抽象状态类
+---------------------+
| + handle() |
+---------------------+
^
|
+----------------------+ +----------------------+
| ConcreteStateA | | ConcreteStateB | // 具体状态类
+----------------------+ +----------------------+
| + handle() | | + handle() |
+----------------------+ +----------------------+
组成部分
-
State
(抽象状态类)- 定义了所有状态的通用接口,例如
insertCoin()
、selectProduct()
等。
- 定义了所有状态的通用接口,例如
-
ConcreteState
(具体状态类)- 实现
State
接口,定义与特定状态相关的行为。
- 实现
-
Context
(上下文类)- 持有一个状态对象,调用当前状态的行为。
- 负责在运行时切换状态对象。
案例:自动售货机
需求描述
设计一个简单的自动售货机,支持以下功能:
- 无硬币状态:用户不能选择商品。
- 有硬币状态:用户可以选择商品。
- 售货状态:用户选择商品后,售货机出货。
目标
- 实现售货机的状态管理,使得行为随着状态变化而动态改变。
- 状态切换应简单且易于扩展。
完整代码实现
以下是状态模式在自动售货机中的应用,输出为中文,附带详细注释。
#include <iostream>
#include <memory>
#include <string>
// 抽象状态类
class State {
public:
virtual void insertCoin() = 0; // 投入硬币
virtual void selectProduct() = 0; // 选择商品
virtual void dispenseProduct() = 0; // 出货
virtual ~State() = default;
};
// 前向声明:解决状态类互相引用的问题
class VendingMachine;
class HasCoinState;
// 上下文类:自动售货机
class VendingMachine {
private:
std::shared_ptr<State> currentState; // 当前状态
public:
void setState(std::shared_ptr<State> state) {
currentState = state; // 切换状态
}
// 调用当前状态的方法
void insertCoin() {
currentState->insertCoin();
}
void selectProduct() {
currentState->selectProduct();
}
void dispenseProduct() {
currentState->dispenseProduct();
}
};
// 具体状态类:无硬币状态
class NoCoinState : public State {
private:
VendingMachine* machine; // 上下文
public:
explicit NoCoinState(VendingMachine* machine) : machine(machine) {}
void insertCoin() override {
std::cout << "硬币已投入,进入有硬币状态。" << std::endl;
machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine))); // 切换到有硬币状态
}
void selectProduct() override {
std::cout << "请先投入硬币。" << std::endl;
}
void dispenseProduct() override {
std::cout << "请先投入硬币并选择商品。" << std::endl;
}
};
// 具体状态类:有硬币状态
class HasCoinState : public State {
private:
VendingMachine* machine;
public:
explicit HasCoinState(VendingMachine* machine) : machine(machine) {}
void insertCoin() override {
std::cout << "硬币已存在,请选择商品。" << std::endl;
}
void selectProduct() override {
std::cout << "商品已选择,正在出货。" << std::endl;
machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine))); // 模拟出货后切换到无硬币状态
}
void dispenseProduct() override {
std::cout << "请先选择商品。" << std::endl;
}
};
// 客户端代码
int main() {
// 创建自动售货机并设置初始状态
VendingMachine machine;
machine.setState(std::make_shared<NoCoinState>(&machine));
// 测试售货机的功能
std::cout << "=== 测试场景 1:无硬币状态 ===" << std::endl;
machine.selectProduct(); // 未投硬币时选择商品
machine.insertCoin(); // 投入硬币
std::cout << "\n=== 测试场景 2:有硬币状态 ===" << std::endl;
machine.selectProduct(); // 投币后选择商品
return 0;
}
运行结果
=== 测试场景 1:无硬币状态 ===
请先投入硬币。
硬币已投入,进入有硬币状态。
=== 测试场景 2:有硬币状态 ===
商品已选择,正在出货。
代码解读
1. 抽象状态类(State
)
class State {
public:
virtual void insertCoin() = 0;
virtual void selectProduct() = 0;
virtual void dispenseProduct() = 0;
virtual ~State() = default;
};
- 定义了所有状态的接口。
- 子类实现这些方法来处理具体的状态逻辑。
2. 上下文类(VendingMachine
)
class VendingMachine {
private:
std::shared_ptr<State> currentState;
public:
void setState(std::shared_ptr<State> state) {
currentState = state;
}
void insertCoin() {
currentState->insertCoin();
}
void selectProduct() {
currentState->selectProduct();
}
void dispenseProduct() {
currentState->dispenseProduct();
}
};
- 持有当前状态对象,并将行为委托给当前状态。
- 通过
setState
方法切换状态。
3. 具体状态类
- 无硬币状态:
void insertCoin() override {
std::cout << "硬币已投入,进入有硬币状态。" << std::endl;
machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine)));
}
- 有硬币状态:
void selectProduct() override {
std::cout << "商品已选择,正在出货。" << std::endl;
machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine)));
}
每个具体状态实现了当前状态的行为逻辑,同时可以切换到其他状态。
状态模式的应用场景
-
对象行为取决于状态:
- 自动售货机、订单状态管理、游戏角色状态(如站立、跑动、跳跃等)。
-
需要消除复杂条件判断:
- 替代
if-else
或switch
语句,将状态逻辑分散到独立的状态类中。
- 替代
总结
状态模式是一种非常实用的设计模式,它通过将状态逻辑封装到独立的类中,动态改变对象的行为,从而提高代码的扩展性和可维护性。
核心优势
- 行为动态改变:通过切换状态对象,动态改变对象的行为。
- 易扩展:新增状态只需添加状态类,不影响现有代码。
- 逻辑清晰:每个状态的逻辑集中在对应的状态类中,代码更易于理解和维护。
典型应用
- 自动售货机
- 在线订单状态(待支付、已支付、已发货等)
- 游戏角色状态(站立、奔跑、跳跃等)