允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 --百度百科
简单地说,状态模式就是状态机设计。其主要用于同一个请求,不同条件下执行不同的功能,作用等同于if...else。
在实际编码工作中,经常会遇到一些特殊的场景与普通场景产生的结果不同,逼迫我们不得不使用if来规避。随着时间的推移,这些if使用的越来越多,导致代码晦涩难懂,变成“一坨”,不易维护。此时,我们就应当分析这些特殊的场景,是在哪些特殊条件下产生,能否与普通场景分离,在不影响主干流程的基础上将此场景增加进去。在搞清楚这些条件后,就可以引进状态机进行代码的重构。
电梯在运行过程中,随时都有上电梯、下电梯和等待电梯的需求。这些需求,在电梯空闲、上行或者下行时又会产生不同的动作。例如:
同样的需求,由于电梯运行的状态不同,导致产生不同的动作。那么,如果用代码来实现电梯的运行?
按照往常的编码习惯,通常会写一个电梯运行线程,在线程增加各种if用于处理不同的场景。如此设计会存在一些隐患:
此时,引用状态机就能够很清晰的描述这些场景:
由于代码量较多,本篇仅贴出部分核心的代码片段:
状态表
CElevatorSrv::mStateTable[] =
{
{ LEV1_ANY, LEV2_ANY, SIG_ID_POWER_ON, &CElevatorSrv::MsgRespondInit},
{ LEV1_DOOR_OPEN, LEV2_IDLE, SIG_ID_POWER_OFF, &CElevatorSrv::MsgRespondShutdown},
{ LEV1_DOOR_CLOSE, LEV2_IDLE, SIG_ID_TAKE_UP_ORDER, &CElevatorSrv::MsgRespondOrderUpIdle},
{ LEV1_ANY, LEV2_ANY, SIG_ID_TAKE_UP_ORDER, &CElevatorSrv::MsgRespondOrderUp},
{ LEV1_DOOR_CLOSE, LEV2_IDLE, SIG_ID_TAKE_DOWN_ORDER, &CElevatorSrv::MsgRespondOrderDownIdle},
{ LEV1_ANY, LEV2_ANY, SIG_ID_TAKE_DOWN_ORDER, &CElevatorSrv::MsgRespondOrderDown},
{ LEV1_ANY, LEV2_ANY, SIG_ID_ARRIVE_FLOOR, &CElevatorSrv::MsgRespondArriveFloor},
{ LEV1_ANY, LEV2_IDLE, SIG_ID_EXIT, &CElevatorSrv::MsgRespondExit},
{ LEV1_ANY, LEV2_ANY, SIG_ID_ANY, &CElevatorSrv::MsgRespondIgnore}
};
注:由于在实际场景中,电梯仅允许关门时才可运行。因此这里将开门、关门作为一级状态,电梯运行状态作为二级状态。
消息处理函数
void CElevatorSrv::ProcessMsg(SMsgPacket *pMsg)
{
if (!IsStart()) {
ELEVATORSRV_LOGE("Elevator not start!\n");
return;
}
if (pMsg == nullptr) {
ELEVATORSRV_LOGE("pMsg is nullptr!\n");
return;
}
int index = 0;
EElevatorDoorState curLev1State = GetLev1State();
EElevatorRunState curLev2State = GetLev2State();
EMsgType msgId = pMsg->type;
ELEVATORSRV_LOGD("Get Msg: 0x%x\n", msgId);
// loop: 遍历状态表,进入与状态匹配的入口
do
{
if ( ( (mStateTable[index].lev1State == curLev1State)
|| (mStateTable[index].lev1State == LEV1_ANY)
)
&& ( (mStateTable[index].lev2State == curLev2State)
|| (mStateTable[index].lev2State == LEV2_ANY)
)
&& ( (mStateTable[index].msgId == msgId)
|| (mStateTable[index].msgId == SIG_ID_ANY)
)
)
{
(this->*(mStateTable[index].callback))(pMsg);
break;
}
index++;
} while (1);
}
通过查表的方式,进入匹配当前状态的响应函数。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。