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

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);
    }
}
相关推荐
欢乐小v6 分钟前
elementui-admin构建
前端·javascript·elementui
霸道流氓气质33 分钟前
Vue中使用vue-3d-model实现加载3D模型预览展示
前端·javascript·vue.js
溜达溜达就好40 分钟前
ubuntu22 npm install electron --save-dev 失败
前端·electron·npm
慧一居士1 小时前
Axios 完整功能介绍和完整示例演示
前端
晨岳1 小时前
web开发-CSS/JS
前端·javascript·css
22:30Plane-Moon1 小时前
前端之CSS
前端·css
半生过往1 小时前
前端上传 pdf 文件 ,前端自己解析出来 生成界面 然后支持编辑
前端·pdf
晨岳1 小时前
web开发基础(CSS)
前端·css
.又是新的一天.1 小时前
前端-CSS (样式引入、选择器)
前端·css