一个非常有用的设计模式——有限状态机

hello大家好,我是前端小嘎

大家有没有做过流程管理,比说做抢火车票抢红包,或者多弹窗这样的场景,如果用if else来管理,那么最后就是一团乱麻,而如果我们用有限状态机来管理就非常的简洁明了了。

什么是有限状态机

有限状态机 (Finite State Machine, FSM),是一种数学计算模型 。它被用来描述一个系统在任何给定时间点都只能处于有限个状态 中的一个 ,并且能够根据输入事件 从一个状态转换到另一个状态。


有限状态机的核心要素

一个典型的有限状态机由以下几个核心部分组成:

  • 状态 (States) :系统可能存在的有限数量的不同情况或模式。例如,一个交通灯有"红灯"、"黄灯"和"绿灯"三种状态。
  • 初始状态 (Initial State) :系统在开始时所处的特定状态。
  • 输入/事件 (Inputs/Events) :导致状态机从一个状态转换到另一个状态的触发条件。这些可以是外部信号、用户操作或内部条件。例如,交通灯的输入可能是定时器达到某个值,或者车辆传感器检测到有车。
  • 转换 :从一个状态到另一个状态的规则或路径。每次转换都会由一个特定的输入或事件触发。例如,从"红灯"到"绿灯"的转换。
  • 输出:某些FSM在状态转换或进入某个状态时会产生相应的输出。例如,一个自动售货机在完成交易后可能会输出商品。

解决一个抢红包的场景

分析问题

流程如下 :判断红包是否存在,若存在点击红包=>出现红包弹窗=>点击领取=>出现结果弹窗=>点击关闭弹窗=>回归初始状态,继续监听新红包

先写一个State的抽象类

所有状态必须实现3个方法

onEnd

状态离开时执行

onEnter:

状态进入时执行

onUpdate:

状态激活时一直调用

js 复制代码
import StateMachine from "./StateMachine";

abstract class AbstractState {
    abstract name: StateEnum;
    onEnd() {
    }
    onEnter() {
    }
    onUpdate() {
    }
}

export enum StateEnum {
    INIT = "init",
    CHECK = "check",
    PAY = "pay",
    CLAIM = "claim",
    SEARCH = "search",
}

export default AbstractState;

接着定义初始状态

这里在查找到红包就可以进入claim 状态了

js 复制代码
import { findObjectsThen, findPic } from "../utils/findPic";
import State, { StateEnum } from "../State";
import StateMachine from "../StateMachine";

class InitState extends State {
    name = StateEnum.INIT;
    async onUpdate() {
        super.onUpdate();
        findObjectsThen([红包])], (UiObject) => {
            const comp = UiObject.bounds();
            //图像匹配
            let pos = findPic("", [comp.left, comp.top, comp.right - comp.left, comp.bottom - comp.top]);
            if (pos) {
                let res = click(pos.x, pos.y);
                // 发现红包后跳到下一个状态
                res && StateMachine.pushState(StateEnum.CLAIM);
            } else {
                //没发现红包就等会
                sleep(60 * 1000);
            }
        });
    }
}

export default new InitState();

关键来了

我们需要弄一个状态管理器,在管理器内部保存这当前状态,还有跳转到下一阶段的静态方法,当跳转时会执行前一个状态的onEnd,后一个状态的onEnter

js 复制代码
import State, { StateEnum } from "./State";

class StateMachine {
    public currentState: State | null;
    map: Map<StateEnum, State>;
    constructor() {
        this.currentState = null;
        this.map = new Map();
    }

    pushState(name: StateEnum) {
        if (this.map.has(name)) {
            let newState = this.map.get(name) as State;
            this.currentState?.onEnd();
            this.currentState = newState;
            this.currentState.onEnter();
        }
    }
}

export default new StateMachine();

然后我们在main方法中组装

js 复制代码
function main() {
    if (!requestScreenCapture()) {
        toast("请求截图失败");
        exit();
    }
    machine.init([InitState, ClaimState, PayState, CheckState], StateEnum.INIT);

    while (1) {
        sleep(2000);
        machine.currentState?.onUpdate();
        sleep(2000);
    }
}
相关推荐
小菜全5 小时前
《WebAssembly:前端开发的新可能》
前端·javascript
余防5 小时前
CSRF跨站请求伪造
前端·安全·web安全·csrf
兮山与5 小时前
前端2.0
前端
南风木兮丶5 小时前
Vue 项目安装 @antfu/eslint-config 保姆级教程
前端·javascript·vue.js
万少5 小时前
记 HarmonyOS 开发中的一个小事件 怒提华为工单
前端·harmonyos
未来之窗软件服务5 小时前
万象EXCEL开发(六)excel单元格运算逻辑 ——东方仙盟金丹期
前端·excel·仙盟创梦ide·东方仙盟·万象excel
Mintopia5 小时前
🚀 Cesium-Kit:10 秒为你的 Cesium 项目添加动态光效标记
前端·javascript·cesium
Mintopia5 小时前
🌩️ 云边协同架构下的 WebAI 动态资源调度技术
前端·javascript·aigc
Olrookie5 小时前
若依前后端分离版学习笔记(十六)——scoped、路由跳转
前端·笔记
qaqxiaolei5 小时前
高效办公利器:前端实现表格导出excel格式 + 自定义水印的完整方案
前端·javascript