TypeScript实现一个有限状态机

开放封闭原则:

开发过程中,因为变化、升级和维护等原因需要对原有逻辑进行修改时,很有可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有功能新测试。

我们应该尽量通过扩展实体的行为来实现变化,而不是通过修改已有的代码来实现变化

具体一点呢 —— 类、模块和函数应该对扩展开放,对修改关闭。模块应该尽量在不修改原代码的情况下进行扩展。

核心 —— 用抽象构建框架,用实现扩展细节。

总结一下 —— 开发人员应该对程序中呈现的频繁变化的那些部分作出抽象,然后从抽象派生的实现类来进行扩展,当代码发生变化时,只需要根据需求重新开发一个实现类来就可以了。要求我们对需求的变更有一定的前瞻性和预见性,同时拒绝对于应用程序中的每个部分都刻意的进行抽象。

设计模式6大原则:

单一原则 (SRP): 实现类要职责单一, 一个类只做一件事或者一类事,不要将功能无法划分为一类的揉到一起

里氏替换原则(LSP): 不要破坏继承体系,子类可以完全替换掉他们所继承的父类,可以理解为调用父类方法的地方换成子类也可以正常执行调用

依赖倒置原则(DIP): 如果某套功能或者业务逻辑可能之后会出现并行的另外一种模式或者较大的调整,那不如把这部分逻辑抽象出来,创建一个包含相关方法的抽象类,而实现类继承这个抽象类来重写抽象类中的方法,完成具体的实现,调用这些功能方法的类不需要关心自己调用的这些个方法的具体实现,只管调用这些抽象类中定义好的形式上的方法即可,不与实际实现这些方法的类发生直接依赖关系,方便之后的实现逻辑的替换更改;

接口隔离原则(ISP) : 在设计抽象类的时候要精简单一, 白话说就是,A需要依赖B提供的一些方法,A我只用B的3个方法,B就尽量不要给A用不到的方法啦;

迪米特法则(LoD) :降低耦合, 尽量减少对象之间的直接的交互,如果其中一个类需要调用另一个类的某一个方法的话,可通过一个关系类发起这个调用,这样一个模块修改时,就可以最大程度的减少波及。

开放-封闭原则(OCP) :告诉我们要对扩展开放,对修改关闭,你可以继承扩展我所有的能力,但是别动我本人

状态模式:

允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。

用ts手写一个简单的状态模式:

//把每一个状态分支(一个状态对应的行为)放进一个独立的类中
const Actions = {
    todo1: function () {
        console.log('todo1');
    },
    todo2: function () {
        console.log('todo2');
    },
    todo3: function () {
        console.log('todo3');
    }
};

class StateMachine {
    //当前待执行的状态序列
    private _currentState = [];
    constructor() {
        this._currentState = [];
    }
    //改变内部状态的接口
    public changeState(stateList: string[]): StateMachine {
        this._currentState = [];
        for (let state of stateList) {
            this._currentState.push(state);
        }
        return this;
    }
    //实现内部状态分支的接口
    public runState(): void {
        for (let state of this._currentState) {
            Actions[state] && Actions[state].apply(this);
        }
    }
}
//test
let obj = new StateMachine();
obj.changeState(['todo1', 'todo2']).runState();//通知执行'todo1', 'todo2'两种状态

状态模式的优缺点

优点:一个状态对应的行为作为一个方法封装在一个类里,直观清晰,互相独立,不用写很多的if else

缺点:需要考虑状态行为分支拆分的程度 避免过度设计反而降低代码可读性

 

状态模式的一个模型-有限状态机

github上有一个有限状态机的函数库javascript-state-machine

https://github.com/jakesgordon/javascript-state-machine

ts代码示例:实现一个有限状态机:

import * as StateMachine from 'javascript-state-machine';
let _fsm = StateMachine.create({
    //红绿灯状态机的初始状态
    initial: 'green',
    //触发状态改变的各种事件,比如greenToYellow事件使green状态变成yellow状态
    events: [
        { name: 'greenToYellow', from: 'green', to: 'yellow' },
        { name: 'yellowToRed', from: 'yellow', to: 'red' },
        { name: 'redToYellow', from: 'red', to: 'yellow' },
        { name: 'yellowToGreen', from: 'yellow', to: 'green' }
    ],
    //js-state-machine可以为每个事件指定两个回调函数  onbeforexxx在xxx事件发生前触发 onafterxxx|onxxx在xxx事件发生后触发
    //js-state-machine可以为每个状态指定两个回调函数  onleavexxx在离开xxx状态时触发 onenterxxx在进入xxx状态时触发
    callbacks: {
        //以greenToYellow事件为例
        onbeforegreenToYellow: () => {
            console.log('greenToYellow 准备触发');
        },
        onaftergreenToYellow: () => {
            console.log('greenToYellow 触发了');
        },
        //以green状态为例
        onleavegreen: () => {
            console.log('离开green状态');
        },
        onentergreen: () => {
            console.log('进入green状态');
        },
        //除了为每个事件和状态单独指定回调函数 还可以为所有的事件和状态指定通用的回调函数
        onbeforeevent: () => {
            // console.log('任一事件发生前触发');
        },
        onafterevent: () => {
            // console.log('任一事件结束后触发');
        },
        onleavestate: () => {
            // console.log('任一状态离开时触发');
        },
        onenterstate: () => {
            // console.log('任一状态进入时触发');
        },
        error: function (eventName, from, to, args, errorCode, errorMessage) {
            console.log('event ' + eventName + ': ' + errorMessage);
        }
    }
});
console.log('_fsm.current=' + _fsm.current);//green
//a.同步状态改变
// _fsm.greenToYellow();
// console.log('_fsm.current=' + _fsm.current);//green
//b.如果希望在离开green状态时先完成异步操作再离开 可以使用transition方法
_fsm.onleavegreen = function () {
    setTimeout(() => {
        _fsm.transition();
        console.log('_fsm.current after setTimeout=' + _fsm.current);//yellow
    }, 1000);
    return StateMachine.ASYNC;
};
_fsm.greenToYellow();
console.log('_fsm.current=' + _fsm.current);//green

// _fsm.redToYellow();//直接调用会报错 因为当前状态不是red 那如何判断呢?可以用下面的查询方法
console.log(_fsm.is('red'));//返回状态机当前状态是不是'red'
console.log(_fsm.can('redToYellow'));//返回'redToYellow'这个事件能否在当前状态下执行

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来给您写一个TypeScript 实现的按钮,点击按钮后使用 `adb shell` 命令: ```typescript class AdbButton { private button: HTMLButtonElement; constructor() { this.button = document.createElement('button'); this.button.textContent = '执行 adb shell 命令'; this.button.addEventListener('click', () => this.executeAdbShellCommand()); document.body.appendChild(this.button); } private executeAdbShellCommand(): void { const command = 'adb shell <your command>'; // 请将 <your command> 替换为您要执行的命令 const process = Deno.run({ cmd: ['sh', '-c', command] }); process.status().then((status) => { if (status.success) { console.log('命令执行成功!'); } else { console.log('命令执行失败!'); } }); } } const adbButton = new AdbButton(); ``` 这个类的构造函数会创建一个按钮,并将按钮添加到页面中。当按钮被点击时,它会使用 `Deno.run` 函数执行一个基于 `adb shell` 命令的子进程。请将 `<your command>` 替换为您要执行的命令。 执行完命令后,程序会根据子进程的执行状态输出相应的提示信息。 请注意,由于 `Deno.run` 函数需要访问系统资源,因此需要在代码中声明访问权限。如果您使用浏览器环境,则需要在页面中添加以下标记: ```html <meta name="deno-permissions" content="allow-run" /> ``` 这将允许程序在浏览器中使用 `Deno.run` 函数。 如果您使用的是 Node.js 环境,则不需要添加此标记。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值