状态模式
使用场景
在一个类的内部会存在多种状态的变化,状态的变化会引起对象的行为、动作或属性发生变化,在类的外部就如类的自身发生了变化。此时可以使用状态模式对类在不同状态下的变化进行管理,增加代码的可读性。
典型案例
对于一个类,其一天内的工作内容会随着时间的变化而变化,此时若将工作视为该类的一个成员函数,该成员函数的动作会随着时间改变而改变。为增加代码的复用性,可将状态定义为一个基类,在定义不同时间段的子类时继承该基类,并在子类中针对不同的状态对基类中的虚函数进行重载。
如
class Sanji;//仅声明,供定义时调用
class AbstractState
{
public:
virtual void working(Sanji* sanji) = 0;
virtual ~AbstractState() {}
};
其中,working函数为纯虚函数,可在子类中进行重载,以实现对不同状态下的工作进行差异化定义。
再定义不同时间段的子类,如
// 上午状态
class ForenoonState : public AbstractState
{
public:
void working(Sanji* sanji) override;
};
// 中午状态
class NoonState : public AbstractState
{
public:
void working(Sanji* sanji) override;
};
// 下午状态
class AfternoonState : public AbstractState
{
public:
void working(Sanji* sanji) override;
};
// 晚上状态
class EveningState : public AbstractState
{
public:
void working(Sanji* sanji) override;
};
定义成员函数
#include"State.h"
#include"sanji.h"
#include<iostream>
using namespace std;
void ForenoonState::working(Sanji* sanji)
{
int time = sanji->getClock();
if (time < 8)
{
cout << "当前时间<" << time << ">点, 准备早餐, 布鲁克得多喝点牛奶..." << endl;
}
else if (time > 8 && time < 11)
{
cout << "当前时间<" << time << ">点, 去船头钓鱼, 储备食材..." << endl;
}
else
{
sanji->setState(new NoonState);
sanji->working();
}
}
void NoonState::working(Sanji* sanji)
{
int time = sanji->getClock();
if (time < 13)
{
cout << "当前时间<" << time << ">点, 去厨房做午饭, 给路飞多做点肉..." << endl;
}
else
{
sanji->setState(new AfternoonState);
sanji->working();
}
}
void AfternoonState::working(Sanji* sanji)
{
int time = sanji->getClock();
if (time < 15)
{
cout << "当前时间<" << time << ">点, 准备下午茶, 给罗宾和娜美制作爱心甜点..." << endl;
}
else if (time > 15 && time < 18)
{
cout << "当前时间<" << time << ">点, 和乔巴去船尾钓鱼, 储备食材..." << endl;
}
else
{
sanji->setState(new EveningState);
sanji->working();
}
}
void EveningState::working(Sanji* sanji)
{
int time = sanji->getClock();
if (time < 19)
{
cout << "当前时间<" << time << ">点, 去厨房做晚饭, 让索隆多喝点汤..." << endl;
}
else
{
cout << "当前时间<" << time << ">点, 今天过得很高兴, 累了睡觉了..." << endl;
}
}
定义sanji类实现获取时间、修改状态以及调用工作接口
#pragma once
#include"sanji.h"
#include"State.h"
class Sanji
{
public:
Sanji()
{
m_state = new ForenoonState;
}
void working()
{
m_state->working(this);
}
void setState(AbstractState* state)
{
if (m_state != nullptr)
{
delete m_state;
}
m_state = state;
}
void setClock(int time)
{
m_clock = time;
}
int getClock()
{
return m_clock;
}
~Sanji()
{
delete m_state;
}
private:
int m_clock = 0; // 时钟
AbstractState* m_state = nullptr;
};
主函数中模拟时间变化,保存时间点并通过for循环对时间点进行遍历,做出对应工作
#include<iostream>
#include<vector>
#include"sanji.h"
#include"State.h"
using namespace std;
int main()
{
Sanji* sanji = new Sanji;
// 时间点
vector<int> data{ 7, 10, 12, 14, 16, 18, 22 };
for (const auto& item : data)
{
sanji->setClock(item);
sanji->working();
}
delete sanji;
return 0;
}
运行结果
在不同的状态中,通过当前时间判断是否需要进行状态变化,并重新对sanji类中的状态进行更新。
状态机
状态机可以理解为是一种描述事物不同状态的数学模型,用于表示事物在什么情况下状态会发生改变并在该状态下会执行什么动作。状态模式是一种设计代码的方法,我们可以使用状态模式的思想来对状态机这一数学模型进行代码实现。
如上述例子使用状态机绘制出状态变化的过程即为一条状态随时间变化的单向状态变化图。