【有限状态机】- FSM详细讲解 【附Autoware有限状态机模型代码讲解】

参考博客:
(1)FSM(有限状态机)
(2)关于有限状态机(FSM)的一些思考
(3)状态设计模式

1 状态机简介

有限状态机FSM:有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

状态机思维:将一个事件分成多个完整的状态,每个状态通过输入和输出进入下一个状态。

优点:可以把模型的多状态和多状态之间的转换条件解耦,降低程序耦合度,让程序维护变的更加容易

FSM术语:state、transition、action、transition condition

2 FSM术语

2.1 状态 State

将一件事拆分为多件事,为每一件事赋予一个名字,这个名字就被称为FSM中的状态。因其状态有限,所以FSM被称为有限状态机

  • 风扇:划分为关、一档、二挡、三挡等状态
  • 电梯门:划分为关闭、正在开启、开启、正在关闭
2.2 状态转移

状态转移:一个状态执行了某些动作转变为另一个状态的过程

2.3 转移条件 Event

在某个状态下,达到了某个转移条件,才会按照状态机的转移流程转移到下一状态,并执行相应的动作

2.4 动作 Action

当转变为一个新状态时,在这个状态下需要做的事情,称为动作

3 有限状态机的实现方式

实现方式 适用场景 优点 缺点
分支逻辑法 适用于条件简单,状态固定,没有新增和扩展的需求 状态机代码直译,简单直接,状态逻辑比较集中,容易查看 对于较复杂的状态机,这种方式容易遗漏或者写错。大量的if-else和switch-case代码分支判断逻辑,可读性和可扩展性比较差,对新增和修改的场景容易引入bug
查表法 通过二维数组来表达状态机,适用于复杂状态机,执行动作比较固定和简单的场景,比如游戏这种状态比较多的场景就适合用查表法 相对于分支逻辑的实现方式,查表法的代码实现更加清晰,可读性和可维护性更好 遇到比较复杂的动作,就无法通过简单的二维数组表示了,有一定的局限性
状态模式 对于状态并不多、状态转移也比较简单,但事件触发执行的动作包含的业务逻辑可能比较复杂的状态机来说,我们首选这种实现方式 代码结构更清晰,可以规避过多的分支逻辑判断,代码可维护性更高 状态模式会引入很多状态类,如果状态颗粒度控制不好,会导致状态类爆炸问题;另外逻辑比较分散,集中在状态类中,无法在一个地方整体看出整个状态机的逻辑
3.1 示例FSM

三种状态:未支付、未收货、完成
两种事件:支付、收货
状态的流转即动作:支付后状态为未收货,收货后状态为完成

支付状态枚举

// 支付状态枚举
enum PayState {
   
   
	// 待支付
	UNPAY = 0,
	// 待收货
	UNRECEIVE = 1,
	// 完成
	FINAL = 2,
	// 错误
	ERROR = 3,
};

支付事件枚举

// 支付事件枚举
enum PayEvent {
   
   
	// 支付
	Pay = 0,
	// 收货
	RECEIVE = 1,
};
3.2 分支逻辑法
#include <iostream>

using namespace std;

// 支付状态枚举
enum PayState {
   
   
	// 待支付
	UNPAY = 0,
	// 待收货
	UNRECEIVE = 1,
	// 完成
	FINAL = 2,
	// 错误
	ERROR = 3,
};

// 支付事件枚举
enum PayEvent {
   
   
	// 支付
	PAY = 0,
	// 收货
	RECEIVE = 1,
};

class PayStateMachine {
   
   
	private:
		PayState payState;
	public:
		PayStateMachine() {
   
   
			payState = PayState::UNPAY;
		}
		void executeEvent(PayEvent payEvent) {
   
   
			switch (payEvent) {
   
   
				// 支付行为发生:未支付 -> 待收货
				case PAY:
					if (this->payState != PayState::UNPAY) {
   
   
						cout << "商铺不是【未支付】状态,请核验" << endl;
						this->payState = PayState::ERROR;
						break;
					} 
					this->payState = PayState::UNRECEIVE;
					break;
				// 收货行为发生:待收货 -> 完成
				case RECEIVE:
					if (this->payState != PayState::UNRECEIVE) {
   
   
						cout << "商铺不是【未收货】状态,请核验" << endl;
						this->payState = PayState::ERROR;
						break;
					}
					this->payState = PayState::FINAL;
					break;
				default:
					cout << "未设置的行为" << endl;
					break;
			}
		}
		PayState getCurrentState() {
   
   
			return this->payState;
		}
};

int main() {
   
   
	PayStateMachine payStateMachine;
	cout << "当前状态为:" << payStateMachine.getCurrentState() << endl;
	payStateMachine.executeEvent(PayEvent::RECEIVE);
	cout << "当前状态为:" << payStateMachine.getCurrentState() << endl;
	return 0;
}

运行结果:

当前状态为:0
商铺不是【未收货】状态,请核验
当前状态为:3

3.3 查表法

将状态和事件形成一个二维矩阵表,将结果态放入其中

UNPAY UNRECEIVE FINAL
PAY 状态转为UNRECEIVE ERROR ERROR
RECEIVE ERROR 状态转为FINAL ERROR

将结果态转为一个二维数组进行存储,用的时候使用状态和事件枚举的value值作为索引,获取结果态

#include <iostream>
#include <vector>
using namespace std;

// 支付状态枚举
enum PayState {
   
   
	// 待支付
	UNPAY = 0,
	// 待收货
	UNRECEIVE = 1,
	// 完成
	FINAL = 2,
	// 错误
	ERROR = 3,
};

// 支付事件枚举
enum PayEvent {
   
   
	// 支付
	PAY = 0,
	// 收货
	RECEIVE = 1,
};

class PayStateMachine {
   
   
	public:
		// 初始化
		PayStateMachine() {
   
   
			currentPayState = PayState::UNPAY;
		}
		// 使用状态和事件枚举的value值作为索引,获取结果态
		void executeEvent(PayEvent payEvent) {
   
   
			this->currentPayState = payStateTable[this->currentPayState][payEvent];
		}
		// 获取当前状态
		PayState getCurrentState() {
   
   
			return this->currentPayState;	
		}
	private:
		// 当前状态
		PayState currentPayState;
		// 将结果态转为一个二维数组进行存储
		vect
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值