参考
状态机要素
-
状态:状态的数据和含义。
-
事件:触发状态转换的条件,即从一个状态转移到另一个状态所需的条件。
-
动作:每个状态下需要执行的动作操作。
状态机设计模型
-
分支逻辑法
用 switch 描述状态转移,用 if-else 判断状态转移条件,用函数指针指定状态执行的动作。
这是状态机的简单实现方式,但是当业务需求持续增加,这样的状态机模型的维护成本也大大提高。
-
表驱动法
用数据结构来存储状态及其对应的转换条件和动作。通过查表根据当前状态和输入事件找到下一个状态和动作。
事件A | 事件B | 事件C | 事件D | |
状态A | \ | To状态B \ 动作B | To状态C \ 动作C | \ |
状态B | \ | \ | To状态C \ 动作C | To状态D \ 动作D |
状态C | To状态A \ 动作A | \ | \ | To状态D \ 动作D |
状态D | To状态A \ 动作A | To状态B \ 动作B | \ | \ |
-
状态模式
使用面向对象的方式,将状态抽象为类,每个状态类实现自己的行为和状态迁移逻辑。
class StateMachine;
/**
* 状态基类,描述状态的共性
*/
class State {
protected:
StateMachine *machine_;
public:
virtual ~State() {}
void SetMachine(StateMachine *machine) {
machine_ = machine;
}
virtual void Run() = 0;
private:
virtual State* NextState() = 0;
};
/**
* 状态派生类,描述状态具体动作
*/
class StateA : public State {
public:
void Run() override {
std::cout << "StateA run." << std::endl; // 状态动作
auto next_state = NextState(); // 事件判断
if (next_state != nullptr)
machine_->TransitTo(next_state); // 状态转移
}
private:
State* NextState() {
std::cout << "Next state: StateB" << std::endl;
return new StateB;
}
};
class StateB : public State {
public:
void Run() override {
std::cout << "StateB run." << std::endl; // 状态动作
auto next_state = NextState(); // 事件判断
if (next_state != nullptr)
machine_->TransitTo(next_state); // 状态转移
}
private:
State* NextState() {
std::cout << "Next state: StateA" << std::endl;
return new StateA;
}
};
/**
* 状态机,状态机除了负责状态转移,还存储了各种状态共用的数据,以提供给状态作条件判断。
*/
class StateMachine {
private:
State *current_state_;
public:
StateMachine(State *state) : state_(nullptr) {
TransitTo(state);
}
~StateMachine() {
delete current_state_;
}
void TransitTo(State *state) {
if (current_state_ != nullptr) {
delete current_state_;
}
current_state_ = state;
current_state_->SetMachine(this);
}
void Run() {
current_state_->Run();
}
};
int main(int argc, char** argv) {
StateMachine machine = new StateMachine(StateA);
machine->Run();
}
事件判断可以进一步被分为内部事件和外部事件。内部事件数据信息可以存储在具体状态类自身。外部事件数据信息需要存储在状态机类中,这类信息可能被多个状态类使用到。
之后在状态机业务开发和维护中,仅需要修改具体状态类和状态机类的相关外部事件数据。
开源状态机库
-
StateSmith:跨平台开源工具,用于生成状态机代码。生成的代码是可读的,没有依赖性,适合微控制器。
-
TinyFSM:轻量级有限状态机库,专为实现最佳性能和低内存占用而优化,特别适合实时操作系统环境,框架无法适应多线程开发。
在 TinyFSM 这个框架中,由状态机列表来共同执行任务。一个任务是可以由多个不同对象来共同协作完成。每个对象都是一个状态机,这些状态机组成了状态机列表。在派发事件给到状态机时候,状态机会完成事件动作处理和状态切换。
-
OpenFSM:OpenFSM 被描述为全网最好用的 C++ 有限状态机。