设计模式:状态模式(State Pattern)

状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为,使对象看起来像是修改了它的类。

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. 多线程状态模式的实践

  1. 最小化锁的范围:只在状态转换时加锁,状态处理本身尽量无锁

  2. 避免在状态处理中加锁:防止死锁和性能问题

  3. 考虑使用读写锁:如果读操作远多于写操作

  4. 优先使用不可变状态:避免共享可变状态

  5. 考虑无锁编程:对于高性能场景,可以使用原子操作

  6. 状态转换原子性:确保状态转换是原子操作,避免中间状态

5. 常见问题及解决方案

问题1:状态转换时的竞态条件

  • 解决方案:使用互斥锁或原子操作保护状态转换

问题2:状态处理中的阻塞操作

  • 解决方案:将阻塞操作移到状态对象外部,或使用异步处理

问题3:频繁状态转换导致的性能问题

  • 解决方案:使用无锁设计或减少状态转换频率

状态模式在多线程环境中使用时,关键在于保证状态转换的原子性和状态对象的线程安全性。通过合理的设计,可以构建出既清晰又高效的多线程状态机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值