我們必須記憶一個主體的狀態,以便系統後續進行判斷、操作或其他使用,為此我們也在 Day 03 釐清「狀態」、「個體」的定義。
訂單:提交 → 待付款 → 待發貨→ 待取件→ 交易完成
角色移動模組:站 → 跳 → 站
換個角度觀察
- 獨立的狀態
- 舊狀態 → 新狀態,有明確的轉移路徑
在整理了「主體」、「狀態」後發現,希望一個主體的「現階段的狀態」變成「下一個階段的狀態」時,以範例來看,我們沒辦法單純透 nextState = f(previusState)
,這個方式來達成。
待付款 → 待發貨
中間必須經歷過繳費的行為。
還沒付款不能跳到、轉移到等待發貨的狀態;
站 → 跳
中間必須 按下 B 按鍵的行為時,才能產生。
匍匐臥倒不能直接轉移到跳躍(要先站起來);
以訂單狀態為例,系統背後實作可能是,收到一個來自銀行的帳單紀錄(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 你必須事先定義出
因為要記錄這份表格,我們必須事先列下來,也就是代表我們這個主體的狀態是有限的。