本阶段对状态机编程的理解:
- 把物体的操作逻辑分为几个状态,每个状态有对应的处理
- 操作时,只需要去改变物体状态即可,物体会自动根据状态执行对应的处理
这段理解放入示例中是这样的:
- 电梯有四个状态:开门、关门、运动、停止
- 电梯在达到某个状态时,会输出对应的log作为处理
- 电梯对外有一个goto指定楼层的接口
下面来详细设计一下相关的内容
-
状态[枚举]
- 开门
- 关门
- 运动
- 静止 -
电梯[类]
- 状态标志[变量]
- 设置状态[方法]
- 开门的处理[方法]
- 关门的处理[方法]
- 运动的处理[方法]
- 停止的处理[方法]- 移动到楼层[方法] - 当前楼层[变量] - 目标楼层[变量]
实现代码:
//
// ElevatorOne.hpp
// QFLTest
//
// Created by QuFangliu on 16/9/18.
//
//
/*
电梯测试_1
运行逻辑:
停止->开门(进入)->关门->运动->停止->开门(离开)->关门
????我tm写的什么
*/
#ifndef ElevatorOne_hpp
#define ElevatorOne_hpp
#include <stdio.h>
enum ElevatorOneState
{
State_Opening = 1, //电梯门打开状态
State_Closing = 2, //电梯门关闭状态
State_Running = 3, //电梯运动状态
State_Stopping = 4 //电梯静止状态
};
class ElevatorOne
{
public:
ElevatorOne();
virtual ~ElevatorOne();
public:
void gotoFloor(int nFloor);
private:
void setState(ElevatorOneState eState); //只能通过事件来改变电梯的状态,电梯自动运行
void open(); //开门
void close(); //关门
void run(); //运动
void stop(); //减速到停止
private:
ElevatorOneState m_eState; //电梯状态标志
int m_nTargetFloor; //电梯目标楼层
int m_nFloor; //电梯所在楼层
};
#endif /* ElevatorOne_hpp */
//
// ElevatorOne.cpp
// QFLTest
//
// Created by QuFangliu on 16/9/18.
//
//
#include "ElevatorOne.hpp"
#include <iostream>
#define QFLLOG(_text_) do \
{ \
std::cout << _text_ << std::endl; \
} while (false) \
ElevatorOne::ElevatorOne()
{
m_eState = ElevatorOneState::State_Stopping; //默认静止状态
m_nFloor = 1; //默认楼层1楼
m_nTargetFloor = 1; //默认目标1楼
}
ElevatorOne::~ElevatorOne()
{
}
//操作
void ElevatorOne::gotoFloor(int nFloor)
{
if (m_nTargetFloor == nFloor) {
//不需要运动
QFLLOG("Elevator is here.");
}
else {
//设置目标楼层
m_nTargetFloor = nFloor;
//状态转换
this->setState(ElevatorOneState::State_Opening);
}
}
//状态转换
void ElevatorOne::setState(ElevatorOneState eState)
{
m_eState = eState;
switch (eState) {
case State_Opening:
this->open();
break;
case State_Closing:
this->close();
break;
case State_Running:
this->run();
break;
case State_Stopping:
this->stop();
break;
default:
break;
}
}
//运行逻辑
//开门
void ElevatorOne::open()
{
QFLLOG("Elevator open...");
//下一个状态必然是close
this->setState(ElevatorOneState::State_Closing);
}
//关门
void ElevatorOne::close()
{
QFLLOG("Elevator close...");
//根据目标楼层,判断进入怎样的状态
if (m_nFloor != m_nTargetFloor) {
this->setState(ElevatorOneState::State_Running);
}
else {
//结束状态
}
}
//运行
void ElevatorOne::run()
{
QFLLOG("Elevator run..." << m_nTargetFloor);
//移动到对应的楼层
m_nFloor = m_nTargetFloor;
//下一个状态必然是stop
this->setState(ElevatorOneState::State_Stopping);
}
//静止
void ElevatorOne::stop()
{
QFLLOG("Elevator stop...");
//下一个状态必然是open
this->setState(ElevatorOneState::State_Opening);
}
上面就是Elevator的完整逻辑代码,运行测试过程代码如下,直接放在 int main(void)中即可,我示例中是写在按钮的触发事件中。
//elevator
auto pElevator = new ElevatorOne();
pElevator->gotoFloor(20);
pElevator->gotoFloor(20);//移动到本层试试
输出结果:
Elevator open...
Elevator close...
Elevator run...20
Elevator stop...
Elevator open...
Elevator close...
Elevator is here.
总结:
1.我不知道为什么要写一个gotoFloor的函数,只是觉得在测试中直接去写setState会很别扭,不符合正常控制过程(虽然在其他的博客中看到的关于FSM的实例代码,调用时都是直接写了几个setState)。
2.虽然这里有区分4个状态,每个状态也有对应的处理函数。但是我觉得这里使用状态并没有什么用。因为[使用setState转移到下一个状态],都可以直接用[下一个状态的处理函数]来替换。也就是说
//状态转换 this->setState(ElevatorOneState::State_Opening);
可以直接使用
//状态转换 this->open();
来代替。
求懂FSM的小伙伴评论指点。
网友评论
//状态转换 this->setState(ElevatorOneState::State_Opening);
可以直接使用
//状态转换 this->open();
来代替。
这么做是因为你的状态流程是固定的,有上一个状态自动无需输入就过滤到下一个状态,
如果是玩家, 在站立状态,可能会根据输入来决定是起跳,还是下蹲