状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为,使对象看起来像是修改了它的类。
1. 模式定义
状态模式将状态封装成独立的类,并将动作委托到代表当前状态的对象。当对象的内部状态改变时,它会改变自己的行为,而不需要修改原有类。
2. 模式结构
状态模式包含以下主要角色:
-
Context(上下文):定义客户感兴趣的接口,维护一个ConcreteState子类的实例
-
State(状态):定义一个接口以封装与Context的一个特定状态相关的行为
-
ConcreteState(具体状态):每一个子类实现一个与Context的一个状态相关的行为
3. C++实现示例
下面是一个简单的状态模式实现示例,模拟一个电灯开关的状态变化:
#include <iostream>
#include <memory>
// 前向声明
class State;
// Context类
class LightSwitch {
public:
LightSwitch();
void setState(std::unique_ptr<State> newState);
void turnOn();
void turnOff();
private:
std::unique_ptr<State> currentState;
};
// State抽象基类
class State {
public:
virtual ~State() = default;
virtual void on(LightSwitch* context) {
std::cout << "Light is already on\n";
}
virtual void off(LightSwitch* context) {
std::cout << "Light is already off\n";
}
};
// 具体状态类 - 开状态
class OnState : public State {
public:
OnState() {
std::cout << "Light turned on\n";
}
void off(LightSwitch* context) override;
};
// 具体状态类 - 关状态
class OffState : public State {
public:
OffState() {
std::cout << "Light turned off\n";
}
void on(LightSwitch* context) override;
};
// Context成员函数实现
LightSwitch::LightSwitch() {
currentState = std::make_unique<OffState>();
}
void LightSwitch::setState(std::unique_ptr<State> newState) {
currentState = std::move(newState);
}
void LightSwitch::turnOn() {
currentState->on(this);
}
void LightSwitch::turnOff() {
currentState->off(this);
}
// OnState成员函数实现
void OnState::off(LightSwitch* context) {
std::cout << "Switching light off...\n";
context->setState(std::make_unique<OffState>());
}
// OffState成员函数实现
void OffState::on(LightSwitch* context) {
std::cout << "Switching light on...\n";
context->setState(std::make_unique<OnState>());
}
// 使用示例
int main() {
LightSwitch lightSwitch;
lightSwitch.turnOn(); // 从关到开
lightSwitch.turnOff(); // 从开到关
lightSwitch.turnOff(); // 已经是关状态
return 0;
}
4. 模式优缺点
优点:
-
将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
-
使得状态转换显式化
-
State对象可被共享(如果它们没有内部状态)
缺点:
-
如果状态很少或者几乎不变化,可能会增加不必要的复杂性
-
对于简单的状态机,可能会显得过于繁琐
5. 适用场景
-
一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为
-
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态
6. 与其他模式的关系
-
状态模式与策略模式:两者结构相似,但意图不同。策略模式通常处理算法的替换,而状态模式处理对象状态的改变。
-
状态模式与享元模式:可以结合使用,如果状态对象没有实例变量,它们可以被共享。
状态模式是处理对象状态变化的强大工具,能够使代码更加清晰和易于维护,特别是在处理复杂的状态逻辑时。
通俗化总结
状态模式就像我们日常生活中不同"状态"下行为变化的情景。举几个生活化的例子理解:
1. 电灯开关 - 最经典的例子
想象家中的电灯:
-
关闭状态:按开关→灯亮
-
开启状态:按开关→灯灭
电灯的行为完全取决于它当前的状态,这就是状态模式的核心思想。灯自己知道在不同状态下应该做什么,不需要你额外判断"如果灯是开的就关掉,如果是关的就打开"。
2. 自动售货机
自动售货机有几种典型状态:
-
待机状态:等待投币
-
已投币状态:可以选择商品
-
售出状态:正在出货
-
缺货状态:显示缺货信息
售货机在不同状态下对同一按钮的反应完全不同。比如在"待机状态"按选择按钮不会有反应,但在"已投币状态"按选择按钮就会出货。
3. 交通信号灯
红灯、黄灯、绿灯就是三种不同的状态:
-
红灯:停车等待
-
绿灯:可以通行
-
黄灯:准备变灯
信号灯根据当前状态决定显示什么颜色和下一步要变成什么状态,完全符合状态模式。
4. 游戏角色状态
游戏中的角色可能有:
-
站立状态:按跳跃键→跳起
-
跳跃状态:按跳跃键→无反应(不能二段跳)
-
蹲下状态:按跳跃键→站立
同一个按键在不同状态下产生不同效果,这就是状态模式的典型应用。
5. 手机状态
手机有:
-
锁屏状态:滑动解锁
-
解锁状态:直接进入应用
-
低电量状态:限制某些功能
-
飞行模式:禁用无线功能
通俗总结
状态模式就像人在不同心情下的不同反应:
-
开心时:朋友约吃饭→爽快答应
-
难过时:朋友约吃饭→婉拒
-
忙碌时:朋友约吃饭→改天再说
核心思想:同一个对象(你),在不同状态(心情)下,对同样的请求(约饭)会有不同的反应,而且这些反应规则都封装在各自的状态中,不需要一大堆if-else来判断当前是什么状态该做什么。
这种设计让系统更灵活,新增状态时不需要修改原有代码,只需要添加新的状态类即可。
状态模式在多线程环境中的使用
状态模式在多线程环境中使用时需要特别注意线程安全问题,因为状态可能在多个线程间共享和转换。下面将详细介绍如何安全地在多线程环境中使用状态模式。
1. 基本线程安全考虑
1.1 不可变状态对象
最简单的线程安全方法是使用不可变状态对象:
class State {
public:
virtual ~State() = default;
virtual void handle(Context& context) const = 0;
};
class ConcreteStateA : public State {
public:
void handle(Context& context) const override {
// 处理逻辑
context.changeState(std::make_shared<ConcreteStateB>());
}
};
1.2 状态转换加锁
对于可变状态或需要共享的状态对象,必须使用锁保护状态转换:
class Context {
private:
std::shared_ptr<State> state;
mutable std::mutex mtx;
public:
void changeState(std::shared_ptr<State> newState) {
std::lock_guard<std::mutex> lock(mtx);
state = newState;
}
void request() {
std::lock_guard<std::mutex> lock(mtx);
state->handle(*this);
}
};
2. 高级多线程状态模式实现
2.1 原子状态转换
使用C++11的原子操作可以实现无锁状态转换:
#include <atomic>
#include <memory>
class Context {
private:
std::atomic<std::shared_ptr<State>> state;
public:
void changeState(std::shared_ptr<State> newState) {
state.store(newState);
}
void request() {
state.load()->handle(*this);
}
};
2.2 双重检查锁定模式
对于性能敏感的场景,可以使用双重检查锁定:
class Context {
private:
std::shared_ptr<State> state;
mutable std::mutex mtx;
public:
void changeState(std::shared_ptr<State> newState) {
if (state != newState) {
std::lock_guard<std::mutex> lock(mtx);
if (state != newState) {
state = newState;
}
}
}
};
3. 多线程状态机示例
下面是一个完整的多线程安全状态模式示例,模拟一个网络连接的状态变化:
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
// 前向声明
class State;
class ConnectedState;
class DisconnectedState;
// Context类
class NetworkConnection {
private:
std::shared_ptr<State> state;
mutable std::mutex mtx;
public:
NetworkConnection();
void changeState(std::shared_ptr<State> newState);
void connect();
void disconnect();
void sendData(const std::string& data);
};
// State抽象基类
class State {
public:
virtual ~State() = default;
virtual void connect(NetworkConnection& context) = 0;
virtual void disconnect(NetworkConnection& context) = 0;
virtual void sendData(NetworkConnection& context, const std::string& data) = 0;
};
// 具体状态类 - 已连接状态
class ConnectedState : public State, public std::enable_shared_from_this<ConnectedState> {
public:
void connect(NetworkConnection& context) override {
std::cout << "Already connected\n";
}
void disconnect(NetworkConnection& context) override;
void sendData(NetworkConnection& context, const std::string& data) override {
std::cout << "Sending data: " << data << "\n";
}
};
// 具体状态类 - 未连接状态
class DisconnectedState : public State, public std::enable_shared_from_this<DisconnectedState> {
public:
void connect(NetworkConnection& context) override;
void disconnect(NetworkConnection& context) override {
std::cout << "Already disconnected\n";
}
void sendData(NetworkConnection& context, const std::string& data) override {
std::cout << "Cannot send data - not connected\n";
}
};
// NetworkConnection成员函数实现
NetworkConnection::NetworkConnection() {
state = std::make_shared<DisconnectedState>();
}
void NetworkConnection::changeState(std::shared_ptr<State> newState) {
std::lock_guard<std::mutex> lock(mtx);
state = newState;
}
void NetworkConnection::connect() {
std::lock_guard<std::mutex> lock(mtx);
state->connect(*this);
}
void NetworkConnection::disconnect() {
std::lock_guard<std::mutex> lock(mtx);
state->disconnect(*this);
}
void NetworkConnection::sendData(const std::string& data) {
std::lock_guard<std::mutex> lock(mtx);
state->sendData(*this, data);
}
// ConnectedState成员函数实现
void ConnectedState::disconnect(NetworkConnection& context) {
std::cout << "Disconnecting...\n";
context.changeState(std::make_shared<DisconnectedState>());
}
// DisconnectedState成员函数实现
void DisconnectedState::connect(NetworkConnection& context) {
std::cout << "Connecting...\n";
context.changeState(std::make_shared<ConnectedState>());
}
// 多线程测试函数
void threadFunction(NetworkConnection& conn, int id) {
conn.connect();
conn.sendData("Hello from thread " + std::to_string(id));
conn.disconnect();
}
int main() {
NetworkConnection conn;
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(threadFunction, std::ref(conn), i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}
4. 多线程状态模式的实践
-
最小化锁的范围:只在状态转换时加锁,状态处理本身尽量无锁
-
避免在状态处理中加锁:防止死锁和性能问题
-
考虑使用读写锁:如果读操作远多于写操作
-
优先使用不可变状态:避免共享可变状态
-
考虑无锁编程:对于高性能场景,可以使用原子操作
-
状态转换原子性:确保状态转换是原子操作,避免中间状态
5. 常见问题及解决方案
问题1:状态转换时的竞态条件
-
解决方案:使用互斥锁或原子操作保护状态转换
问题2:状态处理中的阻塞操作
-
解决方案:将阻塞操作移到状态对象外部,或使用异步处理
问题3:频繁状态转换导致的性能问题
-
解决方案:使用无锁设计或减少状态转换频率
状态模式在多线程环境中使用时,关键在于保证状态转换的原子性和状态对象的线程安全性。通过合理的设计,可以构建出既清晰又高效的多线程状态机。
&spm=1001.2101.3001.5002&articleId=148620838&d=1&t=3&u=f31746bd75594d108571f8f39abf686e)
1万+

被折叠的 条评论
为什么被折叠?



