[因为我不懂啊]-什么是状态机编程(设计模式)(2)
先提一下昨天的思路:
- 电梯包含了四种状态:关门,开门,运动,停止
- 状态之间会进行转换:关门(默认)->开门->关门->运动->停止->开门->关门
所以,在代码实现时,就出现了一个[状态(枚举)][电梯(类)],其中[电梯]包含了四种状态的处理逻辑,以及提供了一个[去某一层]的方法。状态之间的转换直接写在状态的处理逻辑中(例如开门状态的处理,就是转换到关门状态)。
然后,以此为基础,再进行今天的状态机的学习。
经过一番查阅,我对状态机的理解有一些改变了:
从电梯的例子来看,电梯的运行过程包含了4个状态。如果我在电梯的gotoFloor函数中,使用
if (当前为关门状态) { 关门状态的处理;下一个状态; } else if (当前为开门状态) {开门状态的处理;下一个状态;} else if (当前为运行状态) {运行状态的处理;下一个状态;} ...
这样的方式来进行状态的处理与转换,这也是状态机的一种实现方式。
所以呢?状态机是什么?我的理解就是:状态机就是控制物体状态转换的逻辑,也就是上面这一堆
if () {} else if () {} ...
。
为什么搜到的多数博客中都把状态机描述得很复杂呢?因为状态机本身就是一套状态转换逻辑,只不过它是为了处理更复杂的物体的状态转换而编写的。
所以状态机的运用,在于怎样合理、高效的实现状态的转换,把原本需要使用大量 if .. else if ..
来处理的转换逻辑描述的更清晰。
好了,昨天写的转换看起来还比较清晰(一点也不高端,就是把if...else if...
换成了swith...case...
),但是如果需要扩展新状态,就要改动原来的swich...case...
了。
所以今天换一种实现方式:为每个状态实现一个状态类。
详细设计:
- 状态类的基类
* 一个[执行逻辑处理]函数
* 一个[电梯]对象
* 一个[设置电梯]函数 - 关门[状态类]
* ... - 开门[状态类]
* ... - 运动[状态类]
* ... - 停止[状态类]
* ...
*电梯[类]
* 关门状态类[指针]
* 开门状态类[指针]
* 运动状态类[指针]
* 停止状态类[指针]
* 当前状态类[指针]
* 设置状态[函数]
* 按下电梯[函数]
源文件:
//
// ElevatorTwo.hpp
// QFLTest
//
// Created by QuFangliu on 16/9/19.
//
//
#ifndef ElevatorTwo_hpp
#define ElevatorTwo_hpp
#include <stdio.h>
//预先声明一下,在state中需要用到
class ElevatorTwo;
//Elevator has four status : closing, opening, running, stopping
//Four status correspond four classes. All of them inherited from one base state class.
class ElevatorStateBase
{
public:
virtual ~ElevatorStateBase() {}
//某个状态需要执行的逻辑
virtual void excute() = 0;
//设置电梯类
void setElevator(ElevatorTwo *pElevator) { m_pElevator = pElevator; }
protected:
//保存一个ElevatorTow的指针,为了修改它的状态
ElevatorTwo *m_pElevator;
};
//四种状态的类
//Closing
class ElevatorStateClosing : public ElevatorStateBase
{
public:
ElevatorStateClosing();
virtual ~ElevatorStateClosing();
//执行逻辑
virtual void excute() override;
};
//Opening
class ElevatorStateOpening : public ElevatorStateBase
{
public:
ElevatorStateOpening();
virtual ~ElevatorStateOpening();
//执行逻辑
virtual void excute() override;
};
//Running
class ElevatorStateRunning : public ElevatorStateBase
{
public:
ElevatorStateRunning();
virtual ~ElevatorStateRunning();
//执行逻辑
virtual void excute() override;
};
//Stopping
class ElevatorStateStopping : public ElevatorStateBase
{
public:
ElevatorStateStopping();
virtual ~ElevatorStateStopping();
//执行逻辑
virtual void excute() override;
};
//电梯类
class ElevatorTwo
{
public:
ElevatorTwo();
virtual ~ElevatorTwo();
//对外开放的操作
void gotoFloor();
public:
//每个状态都有一个class
ElevatorStateBase *m_pStateClosing = nullptr;
ElevatorStateBase *m_pStateOpening = nullptr;
ElevatorStateBase *m_pStateRunning = nullptr;
ElevatorStateBase *m_pStateStopping = nullptr;
//设置状态
void setState(ElevatorStateBase *pState) { m_pState = pState; pState->excute();}
private:
//当前状态
ElevatorStateBase *m_pState = m_pStateClosing; //默认关闭状态
};
#endif /* ElevatorTwo_hpp */
//
// ElevatorTwo.cpp
// QFLTest
//
// Created by QuFangliu on 16/9/19.
//
//
#include "ElevatorTwo.hpp"
#include <iostream>
#define QFLLOG(_text_) do \
{ \
std::cout << _text_ << std::endl; \
} while (false) \
//ElevatorStateBase不需要在这里写实现部分了,它是一个抽象类,不能实例化
//Closing
ElevatorStateClosing::ElevatorStateClosing()
{
m_pElevator = nullptr;
}
ElevatorStateClosing::~ElevatorStateClosing()
{
}
void ElevatorStateClosing::excute()
{
//Closing的逻辑部分
//关门之后,进行一些处理
QFLLOG("Closing->关上电梯门,等待有人按电梯");
//处理完成,根据条件判断下一状态
if (rand() % 100 > 50) {
//假设50%几率,电梯里面没有人(这里的关闭可能是之前一轮运行完后的关闭)
QFLLOG("Closing->没有要去的楼层,停在这个状态");
}
else {
//50%几率,有人选择了下一个楼层
QFLLOG("Closing->准备前往下一个目标楼层");
m_pElevator->setState(m_pElevator->m_pStateRunning);
}
}
//Opening
ElevatorStateOpening::ElevatorStateOpening()
{
m_pElevator = nullptr;
}
ElevatorStateOpening::~ElevatorStateOpening()
{
}
void ElevatorStateOpening::excute()
{
//Opening的逻辑部分
//开门之后,进行一些处理
QFLLOG("Opening->打开电梯门");
//处理完成,下一状态就是关门(转换到下一状态这件事,可以由某些条件触发)
QFLLOG("Opening->上客、下客完成,准备关门");
m_pElevator->setState(m_pElevator->m_pStateClosing);
}
//Running
ElevatorStateRunning::ElevatorStateRunning()
{
m_pElevator = nullptr;
}
ElevatorStateRunning::~ElevatorStateRunning()
{
}
void ElevatorStateRunning::excute()
{
//Running的逻辑部分
//运行到指定的楼层
QFLLOG("Running->运行前往指定的楼层(加速,匀速,减速)");
//处理完成,下一状态就是保持电梯停止(状态转换事件可以由条件触发)
QFLLOG("Running->到了指定楼层,准备停止");
m_pElevator->setState(m_pElevator->m_pStateStopping);
}
//Stopping
ElevatorStateStopping::ElevatorStateStopping()
{
m_pElevator = nullptr;
}
ElevatorStateStopping::~ElevatorStateStopping()
{
}
void ElevatorStateStopping::excute()
{
//Stopping的逻辑部分
//保持电梯停止
QFLLOG("Stopping->保持电梯停止(刹车,锁定)");
//处理完成,下一状态就是开门(状态转换可以根据轿箱内是否有人来进行条件触发)
QFLLOG("Stopping->电梯锁定到楼层,准备开门");
m_pElevator->setState(m_pElevator->m_pStateOpening);
}
//电梯类
ElevatorTwo::ElevatorTwo()
{
//初始化自己的几个状态
m_pStateClosing = new ElevatorStateClosing();
m_pStateOpening = new ElevatorStateOpening();
m_pStateRunning = new ElevatorStateRunning();
m_pStateStopping = new ElevatorStateStopping();
//状态类设置"控制"类
m_pStateClosing->setElevator(this);
m_pStateOpening->setElevator(this);
m_pStateRunning->setElevator(this);
m_pStateStopping->setElevator(this);
}
ElevatorTwo::~ElevatorTwo()
{
//注意,要清内存噢
delete m_pStateClosing;
delete m_pStateOpening;
delete m_pStateRunning;
delete m_pStateStopping;
}
void ElevatorTwo::gotoFloor()
{
//用户的操作,必定是上或者下,也就是从这个事件开始改变状态
//事件导致的状态就是开门
this->setState(m_pStateOpening);
}
测试代码:
//初始化电梯
auto pElevator = new ElevatorTwo();
//按电梯咯
pElevator->gotoFloor();
输出结果(因为用到了随机数,所以输出结果类似,但不唯一):
Opening->打开电梯门
Opening->上客、下客完成,准备关门
Closing->关上电梯门,等待有人按电梯
Closing->准备前往下一个目标楼层
Running->运行前往指定的楼层(加速,匀速,减速)
Running->到了指定楼层,准备停止
Stopping->保持电梯停止(刹车,锁定)
Stopping->电梯锁定到楼层,准备开门
Opening->打开电梯门
Opening->上客、下客完成,准备关门
Closing->关上电梯门,等待有人按电梯
Closing->没有要去的楼层,停在这个状态
示例总结:
1.[电梯]类,其实不需要持有各个状态的指针,当需要转换到某个状态时,生成一个新的状态对象即可。
2.需要扩展状态时,例如[运行]状态的逻辑处理中可能需要新增一个[故障]状态,此时只需要新增一个[故障]状态类,然后在[运行]状态逻辑中,满足故障条件时转换到[故障]状态即可。
总结:
1.状态机,就是一套用于控制物体状态转换的逻辑。实现状态机控制的首要条件就是清楚的划分物体的状态。
2.状态机有各种各样的实现方式,实现时需要考虑到子新增状态。