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

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);
    }
}
相关推荐
IT_陈寒13 小时前
Vite静态资源路径这个大坑害我调了一下午
前端·人工智能·后端
狗哥哥13 小时前
分享下我的读书清单
前端
合天网安实验室14 小时前
Log4J2 FilteredObjectInputStream RCE 漏洞分析
前端·黑客
两点王爷14 小时前
使用 GeoServer 发布 SHP 数据并在前端页面加载显示
前端
lhldsg14 小时前
全民健身解决方案软件开发实战:从架构设计到部署指南
java·前端·数据库·小程序
四六的六14 小时前
让 AI 自己去点后台页面,它把我们的库存点没了
前端·人工智能·agent·个人开发·ai编程·ai产品·ai前端
两点王爷14 小时前
Java 与前端加载 MVT 数据:从服务端切片到浏览器渲染
java·前端·状态模式
小码过河.14 小时前
oceanbase中zone和observer、分区表之间关系
前端·数据库·oceanbase
平头哥技术团队16 小时前
Day 16 | 用 ul、ol、li 一个标签,把散资料收成有序的技能清单页
开发语言·前端·html·html5
代码青铜z16 小时前
不会写代码做小程序,模板开发和AI 编程靠谱吗?创业前先看这 5 件事
前端