iT邦幫忙

2021 iThome 鐵人賽

DAY 4
0
Software Development

From State Machine to XState系列 第 4

Day04 - 事件、狀態轉移

我們必須記憶一個主體的狀態,以便系統後續進行判斷、操作或其他使用,為此我們也在 Day 03 釐清「狀態」、「個體」的定義。

範例前情提要

訂單:提交 → 待付款 → 待發貨→ 待取件→ 交易完成
角色移動模組:站 → 跳 → 站

觀點

換個角度觀察

  1. 獨立的狀態
  2. 舊狀態 → 新狀態,有明確的轉移路徑

在整理了「主體」、「狀態」後發現,希望一個主體的「現階段的狀態」變成「下一個階段的狀態」時,以範例來看,我們沒辦法單純透 nextState = f(previusState) ,這個方式來達成。

待付款 → 待發貨 中間必須經歷過繳費的行為
還沒付款不能跳到、轉移到等待發貨的狀態;

站 → 跳 中間必須 按下 B 按鍵的行為時,才能產生。
匍匐臥倒不能直接轉移到跳躍(要先站起來);

1. 那究竟一個狀態是如何轉移到另外一個狀態的呢?

如何讓 待付款 → 待發貨?

以訂單狀態為例,系統背後實作可能是,收到一個來自銀行的帳單紀錄(Call API、 跑 Cron Job 之類的)

如何讓 站立 → 跳躍?

以 RPG 為例, 是接收、監聽到使用者(玩家)按下鍵盤 B 的事件。

我們可以把這兩個東西稱為是種「事件」,我們該如何定義事件呢?

事件是指某些東西,那些東西發生了的話,可能會對系統產生影響、改變...
by Wiki: UML State Machine - Events

而從舊狀態 → 新狀態,就是種改變,而這種改變是受到了什麼影響?我們稱其為「事件」

而在系統中的所有事件,也就是有某些事件會導致我們的狀態轉移。

狀態轉移了,必定代表某些事件發生了;但是某些事件發生,不一定會導致狀態轉移,轉移的條件是根據前一個狀態以及當下發生的事件所決定。

當站著時,按下 ⇩ (keydown) ,才會臥倒匍匐;
當跳躍時,按下 ⇩ (keydown) ,則不會有反應。

到這裡我們似乎有點譜了,既然 nextState = f(previusState) 不成,那 nextState = f(previusState, event) 可以嗎?

f('待付款', '收到帳款')  === '待發貨'
f('站立', '按 B') === '跳躍'

看起來好像有點搞頭了!!

那來思考一下這個 function f 的底層該如何實作呢?我們知道當 舊狀態 → 新狀態時,要經歷了一個事件

什麼事件?特定的事件就像是...
[狀態] 是站著時,[事件] 按下 ⇩ (keydown) ,才會臥倒匍匐;
[狀態] 是跳躍時,[事件] 按下 ⇩ (keydown) ,則不會有反應。

所以我們必定要有一份表格、紀錄,來記錄 什麼狀態什麼事件,才能進行狀態的轉換。
當「對的狀態」 + 「當對的事件」,才能轉換狀態,我們稱這個行為叫做「轉移(Transition)」

所以剛剛的 f 意義上就是那個轉移,語意上的命名也就可以叫 transition

transition('待付款', '收到帳款')  === '待發貨'
transition('站立', '按 B') === '跳躍'

也因為要做出 Transition 這個 mapping 你必須事先定義出

  1. 所有狀態和
  2. 每個狀態配上哪些事件能進行轉移
  3. 這個轉移事件,會將「現階段的狀態」變成哪一個「下一個階段的狀態」

因為要記錄這份表格,我們必須事先列下來,也就是代表我們這個主體的狀態是有限的。

參考文獻


上一篇
Day03 - 個體、對象以及狀態
下一篇
Day05 - 開始、結束與有限狀態機
系列文
From State Machine to XState31

尚未有邦友留言

立即登入留言