• 浅析状态机模式的理解以及如何使用状态机模式简化代码里复杂的 if else 逻辑


      先可以看下这篇博客:如何用状态机简化代码中复杂的 if else 逻辑 —— https://mp.weixin.qq.com/s/dDOA5JQQz3r4a7-yPl33Bg

    一、状态机的基本概念

      当处理的情况特别多,我们把每种情况的处理逻辑封装成一个状态,然后不同情况之间的转换变成状态的转换。这种代码组织形式就是状态机。

      当每个状态知道输入某一段内容时转到哪一个状态,在一个循环内自动进行状态的流转和不同状态的处理,这种叫做状态自动机(automation),如果一个状态在一种输入下只有一个后续状态,这种就叫做确定性有限状态自动机(DFA)。

    二、typescript 源码是怎么利用状态机使流程更清晰的

      首先 tsc 划分了很多状态,每种状态处理一种逻辑。比如:

    • CreateProgram 把源码 parse 成 ast
    • SyntaxDiagnostics 处理语法错误
    • SemanticDiagnostics 处理语义错误
    • Emit 生成目标代码

      typescript 就通过这种状态的修改来完成不同处理逻辑的流转,如果处理到结束状态就代表流程结束。

      这样使得整体流程可以很轻易的扩展和修改,比如想扩展一个阶段,只要增加一个状态,想修改某种状态的处理逻辑,只需要修改下状态机的该状态的转向。而不是大量的 if else 混杂在一起,难以扩展和修改。可以看到,状态机使得 ts 的编译步骤可以灵活的扩展和修改。

    三、词法分析中的状态机

      其实状态机最常用的地方是用于词法分析,因为每个 token 都是一种处理情况,自然会有很多 if else。

      更好的做法是使用状态机(DFA)来做分词,把每一种 token 的处理封装成一个状态。通过边界条件的判断来做状态流转,比如某个 wxml parser 分了这些状态:

      每种状态处理一种情况的 token 的识别:

    四、业务代码中的状态机

      业务代码中当遇到各种 if else 的判断的时候同样可以用状态机来优化。把每种情况封装成一个状态,通过某一种条件触发状态的流转,然后在状态机里面选择不同的状态处理逻辑进行处理。(其实下面总体来说还是用的 switch case结构)

      总结一下:

    1、我们首先明确了状态机的概念:通过不同状态封装不同情况的处理逻辑,通过状态的修改来完成处理逻辑之间的流转。

    2、如果每种状态都知道下一个状态是什么,在一个循环内自动完成状态流转的状态机,就是状态自动机,当状态为有限个时,就是有限状态自动机(DFA)。

    3、typescript compiler 就是通过状态自动机来进行处理,封装了很多个状态,每个状态知道下一个状态是什么,直到处理到终止状态,就结束编译。

    4、词法分析中一般会使用有限状态自动机(DFA)来处理,不同 token 用不同的状态来处理,通过输入字符的不同来做状态的流转,处理完字符串就完成了分词。

    5、业务代码中也经常会有不同情况做不同的处理,这些情况在一定的条件时会做转换的场景,比如类似开始、暂停、结束、重新开始这种。这种代码就很适合用状态机来优化,不然会有很多的 if else。

      总之,当逻辑可以划分为不同的情况,各种情况之间会相互转换的时候就可以用状态机来优化,能够免去大量的 if else,并且代码的可读性、可扩展性、可维护性都会有一个很大的提升。

    五、用状态机模式思想消除代码里复杂的 if else 逻辑

      前一阵开发的一个 web 界面上有很多诸如“按钮隐藏显示”,“边框隐藏显示”,“伸缩” 等效果的切换,在展示不同内容的时候,这些配套的显示控件需要跟着切换不同的状态。迫于进度,使用的是 if..else, 或者 switch..case 的繁杂的 js 代码来实现这些状态的判断和转换。js 代码很快到了 400~500行,变得很难理解。并且我要加入新的状态切换的时候感觉比较困难。今天决心重构,于是忽然联想起状态机 (State Machine) 模式,不正好在这里能用上吗?而 js 中的对象表示语法正好非常方便构造“状态表”和“状态轮换表”,花了1个多小时,完成了这个工作。重构后代码的逻辑可谓豁然开朗,带来的仅仅是配置上的稍许冗余,但是这个完全可以接受的。

    //  状态表定义
    var statusTable  =  {
        '状态1': {
            sizeCode:  1 ,
            headerUrl: ' / test1 / test2',
            bodyUrl: 'about:blank',
            showTitle:  true ,
            showBorder:  true ,
            showMin:  true ,
            showMax:  false ,
            showClose:  true
        },
        '状态3': {
            sizeCode:  2 ,
            headerUrl: ' / test1 / test2',
            bodyUrl: ' / test1 / test2',
            showTitle:  true ,
            showBorder:  true ,
            showMin:  true ,
            showMax:  true ,
            showClose:  true
        },
        '状态4': {
            sizeCode:  3 ,
            headerUrl: ' / test1 / test2',
            bodyUrl: ' / test1 / test2',
            showTitle:  true ,
            showBorder:  true ,
            showMin:  true ,
            showMax:  false ,
            showClose:  true
        },
        '状态2': {
            sizeCode:  2 ,
            headerUrl: ' / test1 / test2',
            bodyUrl: ' / test1 / test2',
            showTitle:  true ,
            showBorder:  true ,
            showMin:  true ,
            showMax:  false ,
            showClose:  true
        }
    };
    //  当前状态码
    var  currentStatusCode  =  '';
    
    //  切换到状态机某个状态   
    function  loadStatus(code){
         var  status  =  statusTable[code]  ||   null ;
         if ( ! status)  return ;
         //  update other status
         //  利用 status 做一系列设置。。如显示隐藏按钮等
        currentStatusCode  =  code;
    }
    
    //  示例函数 foo 和 bar.
    //  一个函数是一套自定义的逻辑,定义一个状态轮换表即可实现。
    function  foo(){
         var  jumpTable  =  {
            '状态1': '',
            '状态2': '状态1',
            '状态3': '状态4',
            '状态4': ''
        };
        loadStatus(jumpTable[currentStatusCode]  ||  '');
    }
    
    function  bar(){
         var  jumpTable  =  {
            '状态1': '状态2',
            '状态2': '',
            '状态3': '状态1',
            '状态4': '状态3'
        };
        loadStatus(jumpTable[currentStatusCode]  ||  '');
    }

      以上代码来源于:https://blog.csdn.net/inelm/article/details/4612936

  • 相关阅读:
    线程范围内共享数据
    Swagger+ springfox +Spring mvc
    Nginx代理实现跨域
    实现AJAX跨域访问方式一
    Tomcat 调优及 JVM 参数优化
    REST接口设计规范
    IDEA里面添加lombok插件,编写简略风格Java代码
    intelliJ idea 使用技巧&方法
    IntelliJ IDEA 创建maven项目
    IntelliJ IDEA + Maven创建Java Web项目
  • 原文地址:https://www.cnblogs.com/goloving/p/15173626.html
Copyright © 2020-2023  润新知