看到上面的这个状态转换图,一般来说我们还想不到用状态机模式去解决,因为太简单了,简单得可能几行代码就处理了。
通常我们一般会使用下面的代码处理状态机流转的需求,
enum Event {
EVENT_LOGIN, // 登录事件
EVENT_LOGOUT, // 登出事件
EVENT_EXPIRED, // 登录过期事件
EVENT_UNKNOWN,
};
enum State {
STATE_START, // 初始状态
STATE_LOGIN, // 登录状态
STATE_UNKNOWN,
};
struct Context {
State state_;
};
void SwitchCaseMethod(Event event, Context &context) {
switch (event) {
case EVENT_LOGIN:
// do something for login event
context.state_ = STATE_LOGIN;
break;
case EVENT_LOGOUT:
// do something for logout event
context.state_ = STATE_START;
break;
case EVENT_EXPIRED:
// do something for expired event
context.state_ = STATE_START;
break;
default:
// ignore
break;
}
}
但是如果状态机的复杂度增加到下面这样,
使用 switch-case 写出来的很可能就会像面条一样了,
当然我们如果做出适当的抽象,可以写出类似于下面的代码,
enum Event {
EVENT_LOGIN, // 登录事件
EVENT_LOGOUT, // 登出事件
EVENT_EXPIRED, // 登录过期事件
EVENT_UNKNOWN,
};
enum State {
STATE_START, // 初始状态
STATE_LOGIN, // 登录状态
STATE_UNKNOWN,
};
struct Context {
State state_;
};
typedef void (*EventHandler)(Context &context);
void LoginHandler(Context &context) {}
void LogoutHandler(Context &context) {}
void ExpiredHandler(Context &context) {}
#define TABLE_SIZE 255
EventHandler EventHandlerTable[TABLE_SIZE] = {0};
void TableDriveInit() {
EventHandlerTable[EVENT_LOGIN] = LoginHandler;
EventHandlerTable[EVENT_LOGOUT] = LogoutHandler;
EventHandlerTable[EVENT_EXPIRED] = ExpiredHandler;
}
void TableDriveMethod(Event event, Context &context) {
if (event >= TABLE_SIZE) {
return;
}
auto handler = EventHandlerTable[event];
if (NULL != handler) {
handler(context);
}
}
代码的可维护性提高了好多,看起来很干净。但是如果事件增加到几十个,状态也增加到几十个,上面的方式也让人吃不消了,那么我们需要引出状态模式解决我们的问题了。
状态模式的定义可以在这里找到,可以发现状态模式似乎和我们的处理逻辑有些差异,那就是抽象的状态方法只有一个 doAction,和我们 login、logout 和 expired 有些差异,那么我们只需要增加几个方法即可,就像下面这样,
enum Event {
EVENT_LOGIN, // 登录事件
EVENT_LOGOUT, // 登出事件
EVENT_EXPIRED, // 登录过期事件
EVENT_UNKNOWN,
};
enum State {
STATE_START, // 初始状态
STATE_LOGIN, // 登录状态
STATE_UNKNOWN,
};
struct Context {
State state_;
};
class AbstractState {
public:
virtual void onLogin(Context &context) {}
virtual void onLogout(Context &context) {}
virtual void onExpired(Context &context) {}
};
class StartState : public AbstractState {
public:
virtual void onLogin(Context &context) {
// do something and transfer to new state
context.state_ = STATE_LOGIN;
}
};
class LoginState : public AbstractState {
virtual void onLogout(Context &context) {
// do something and transfer to new state
context.state_ = STATE_START;
}
virtual void onExpired(Context &context) {
// do something and transfer to new state
context.state_ = STATE_START;
}
};
这样就能满足在每个状态下只做这个状态感兴趣的事情,其他的都可以忽略,这样我们能够应对大部分场景。
上面我们所说的能够的应对大部分场景言外之意就是有些场景还是应对不了,比如事件经常变化的场景就需要改动抽象类,可参考 spring-statemachine 框架,框架把状态、事件和动作的注册进行了抽象,修改时不需要修改基类只需要修改注册的事件、状态和动作。
游戏人工智能编程案例精粹,https://book.douban.com/subject/3081930/
代码大全第十八章,https://book.douban.com/subject/1477390//
知乎这个问题下有很多高质量的回答,https://www.zhihu.com/question/31845498
有赞工作流引擎,https://tech.youzan.com/workflow-engine-in-youzan-devops/
spring-statemachine 实践,https://cloud.tencent.com/developer/article/1534194
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。