状态模式
1.1 分类
(对象)行为型
1.2 提出问题
开发一个糖果贩卖机,当投入硬币按下按钮,糖果机将掉落一枚糖果。当没有投入硬币,直接按下按钮,将会得到请投币的提示。
1.3 解决方案
允许一个对象在其内部状态改变时改变它的行为。对象看起来适合改变了它的类。
1.4 实现类图
- 上下文(Context)保存了对于一个具体状态对象的引用,并会将所有与该状态相关的工作委派给它。
- 状态(State)接口会声明特定于状态的方法。
- 具体状态(Concrete States)会自行实现特定于状态的方法。
- 上下文和具体状态都可以设置上下文的下个状态,并可通过替换连接到上下文的状态对象来完成实际的状态转换。
1.5 示例代码
#include <iostream>
//class CandyMachine;
class State {
protected:
// CandyMachine* m_candyMachine;
public:
//void setContext(CandyMachine* candyMachine) {
// m_candyMachine = candyMachine;
//}
//virtual ~State(){}
virtual void getCandy() = 0;
virtual void returnCoin() = 0;
virtual void putCoin() = 0;
};
class NoCoinState :public State {
virtual void getCandy() override {
std::cout << "请先投币。\n";
}
virtual void returnCoin() override {
std::cout << "你并没有投币。\n";
}
virtual void putCoin()override {
std::cout << "投币成功。\n";
}
};
class HaveCoinState :public State {
virtual void getCandy() override {
std::cout << "糖果投放中......\n";
}
virtual void returnCoin() override {
std::cout << "硬币退回中......\n";
}
virtual void putCoin() override {
std::cout << "重复投币。\n";
}
};
//Context
class CandyMachine {
private:
State* m_currentState;
State* m_haveCoinState;
State* m_noCoinState;
public:
CandyMachine() {
m_haveCoinState = new HaveCoinState();
m_noCoinState = new NoCoinState();
m_currentState = m_noCoinState;
std::cout << "糖果机:当前状态为--->" << typeid(*m_currentState).name() << "。\n";
}
~CandyMachine() {
delete m_haveCoinState;
delete m_noCoinState;
}
void putCoin() {
std::cout << "糖果机:putCoin。\n";
m_currentState->putCoin();
transitionTo(m_haveCoinState);
}
void getCandy() {
std::cout << "糖果机:getCandy。\n";
m_currentState->getCandy();
transitionTo(m_noCoinState);
}
void returnCoin() {
std::cout << "糖果机:returnCoin。\n";
m_currentState->returnCoin();
transitionTo(m_noCoinState);
}
private:
void transitionTo(State* state) {
m_currentState = state;
std::cout << "糖果机:当前状态为--->" << typeid(*m_currentState).name() << "。\n";
}
};
int main()
{
CandyMachine* candyMachine = new CandyMachine;
candyMachine->getCandy();
candyMachine->returnCoin();
candyMachine->putCoin();
candyMachine->getCandy();
candyMachine->returnCoin();
delete candyMachine;
}
1.6 举个栗子
状态模式将根据当前回放状态,让媒体播放器中的相同控件完成不同的行为。
1.7 总结
1.7.1 优点
- 单一职责原则。将与特定状态相关的代码放在单独的类中。
- 开闭原则。无需修改已有状态类和上下文就能引入新状态。
- 通过消除臃肿的状态机条件语句简化上下文代码。
1.7.2 缺点
如果状态机只有很少的几个状态,或者很少发生改变,那么应用该模式可能会显得小题大作。