Day09 - 实作一个状态机 - 2

  1. State Machine 不完备,没有一个变数能帮我记忆当下的状态是什麽,我在使用 transition 时还要另外用一堆变数来储存 state ,感觉不好维护、也不好维持程序码的连贯性
  2. 当 state 跟 event 都是直接使用字串,很容易打错、出现 bug ,所以想要用常数保护起来
  3. 用双层 switch case 好像比较不好读,感觉也不太好重复使用

今天先来针对第 3 点改进,我们用到很多 switch case,回顾一下状态机的定义,一个状态机描述一个实体、对象,根据这点,我们能不能据此做点抽象化呢?既然说状态机有个对象,如果我们尝试着直接用 object 来描述看看呢?

首先一样要有个初始状态

const machineDef = {
  initialState: "站姿、静止",  // 初始状态
};

接着还有状态机底下的所有状态

const machineDef = {
  initialState: "站姿、静止",  // 初始状态
  // 所有状态
  states: {
    "站姿、静止": ?,
    "站姿、移动": ?,
    "跳跃中": ?,
    "俯卧、静止": ?,
    "俯卧、移动": ?,
  },
};

接着状态之後该放入什麽东西呢?我们知道至少还有个转移,nextState = transition(previusState, event),转移是需要状态、事件一起作为输入。

所以上面的 ? 是否可以放入一个 mapping 来存放 eventnextState 的关系。

const machineDef = {
  initialState: "...",  // 初始状态
  // 所有状态
  states: {
    "previusState1": mapping1,
    "previusState2": mapping2,
    "previusState3": mapping3,
  },
};

一般 mapping 的实作常见可以使用 function (如果放在物件的会就是 method),然後该 function 里面可以再用 switch/caseif/else 实作,但...回头来看今天的初衷,就是要避免 switch/case

所幸我们想到,mapping 也可以透过 object literal 替代

所以我们就决定采用 object literal 来制作 eventnextState 的对应关系(mapping)吧!

「事件」这两个字,让我们回想 JavaScript 如何处理、对待事件,以按钮 button 为例,<button onclick="">点我啊~</button> ,我们会对处於被动、等待、监听的状态时,通常会使用 on 这个前缀字 (如同 onClick, onSubmit, onFocus...)

那就尝试在每个 state 下,建立一个名为 on 的 object literal ,里面装有所有的 eventnextState 的关系(即 transition mapping),对应关系的 key / value 怎麽配? previusState 要配 event ,既然前面已经有 previusState1 再加上我们又使用 on 这个前缀字,想必这个 mapping 的 key 必定是使用 event ,而 value 就是剩下来的 nextState

我们知道

const machineDef = {
  initialState: "...",  // 初始状态
  // 所有状态
  states: {
    "previusState1": on:{ 
      "event1": "nextState1",
      "event2": "nextState2",
    },
  },
};

接着要回来继续实作了,为了简单看到 previusState, eventnextState ,彼此之间的关系,我们再度呼叫 State Diagram 出来~~~

https://ithelp.ithome.com.tw/upload/images/20210921/20130721Kh6DzCoDyK.png

const machineDef = {
  initialState: "站姿、静止",
  states: {
    "站姿、静止": {
      on: {
        跳跃: "跳跃中",
        开始移动: "站姿、移动",
        卧倒: "俯卧、静止",
      },
    },
    "站姿、移动": {
      on: {
        停止移动: "站姿、静止",
      },
    },
    跳跃中: {
      on: {
        降落: "站姿、静止",
      },
    },
    "俯卧、静止": {
      on: {
        起身: "站姿、静止",
        开始移动: "俯卧、移动",
      },
    },
    "俯卧、移动": {
      on: {
        停止移动: "俯卧、静止",
      },
    },
  },
};

如此可读性是不是看起来比较提升呢?也大幅减少程序码行数,省掉不必要的 switch / case

那今天针对第三点做了改进

3. 用双层 switch case 好像比较不好读,感觉也不太好重复使用

这个 object 看起来就像是 state Machine 的定义一般,那我们可以怎麽操纵这个物件呢?

关於第二点

2. 当 state 跟 event 都是直接使用字串,很容易打错、出现 bug ,所以想要用常数保护起来

我们可以把 object 的 key 另外再用 const 储存成常数,或是使用 TypeScript 的 enum

剩下第一点,还有第三点当中的重复使用(这里还没多做解释)

1. State Machine 不完备,没有一个变数能帮我记忆当下的状态是什麽,我在使用 transition 时还要另外用一堆变数来储存 state ,感觉不好维护、也不好维持程序码的连贯性

我们可以制作一个 function 然後将 machineDef 这个物件 作为 input,明天就来跟大家分享怎麽建立这个 function!


<<:  Day10 Html常用标签_5

>>:  【Day 10】 讨论 Data Analytics Pipeline - Google Analytics on AWS (整体)

Day2-Start to go

简介 Go 是由 Google 开发的程序语言,於2007发起,在2009正式推,2012年发布第一...

DAY21: NPM模块管理工具

NPM是Node Package Manager的缩写,中文直接翻的话就是Node包管理工具, 比较...

笔记-vscode debug - launch.json 介绍

最近遇到最大的困扰是在实际工作上遇到的情况 越来越难找到教学或是sample可以做参考而解决 还有就...

JS [笔记] Javascript 优良部分、糟糕与不良的部分

优良部份1~5 1. 宽松型别(loose typing)及 易转型 https://codepen...

D-05-串接API ? RestSharp

串接API 相信很多时候开发程序不是只有自己开发就可以了,不论是公司外还是公司内部都有打Api的需求...