昨天講 API 錯誤處理時,我們看到有些 Request 之所以被拒絕,不一定是 Server 壞掉,也可能只是:
系統目前的 State(狀態),本來就不允許這個操作。
所以今天想來分享一個我在 BuJo 開發後期才發現,而且真的覺得很好用的東西:
State Machine(狀態機)!
BuJo 有四種不同的活動模式,流程、分支和邊界情況一多,我腦袋裡很容易全部纏在一起,大家討論時也不一定真的想著同一條流程。
但把 State Machine 畫出來之後,那些原本只存在腦袋裡的東西,直接變成一張大家都看得到的圖!一切突然都通了很多!!!
所以今天就帶大家來看:
State Machine 到底是什麼?一張 State Diagram(狀態圖)又該怎麼看?
State Machine(狀態機),可以先理解成:
描述一個系統可能有哪些 State(狀態),以及這些 State 之間可以怎麼 Transition(狀態轉換)的模型。
首先先分清楚兩個很容易混在一起的東西:
State Machine
→ 狀態與轉換規則的模型
State Diagram
→ 把這套模型畫出來的視覺表示
也就是說:
State Machine 是模型,State Diagram 是把模型畫出來的圖。
先直接看一張最基本的 State Diagram。

這張圖描述的是一個很經典的閘門例子。
今天我們會先用最基本的 Finite State Machine(有限狀態機,FSM) 來理解 State Machine。
所謂「有限」,指的是:
這套模型裡可能出現的 State,是一組有限而且明確的集合。
像圖中的閘門,就只有兩個:
Locked 和 Unlocked。
而圖上的箭頭,正在描述它們之間允許怎麼改變。
接下來就從這張完整的圖開始,由外往內拆。

我們先從最核心的 ① State(狀態) 開始。
圖中的 Locked 和 Unlocked,不是兩個不同的閘門,而是:
同一個閘門在不同時間可能處於的兩種狀態。
Locked 代表現在上鎖,Unlocked 代表現在解鎖。
State 重要的地方在於:它會影響同一個 Event 發生時,系統接下來怎麼反應。
例如同樣都是 push:
Locked 時,閘門仍然保持 Locked
Unlocked 時,則會回到 Locked
所以在有狀態的系統裡,不能只看「發生了什麼」,還要一起看:
「這件事發生時,系統原本在哪個 State?」
知道 State 之後,再來看圖上的 ② Transition(狀態轉換)。
圖二裡連接 State 的箭頭,就是:
② Transition(狀態轉換)。
Transition 描述的是:
系統允許從一個 State,轉換到另一個 State 的規則。
例如圖中的:
Locked → Unlocked
就是其中一條 Transition。
所以 State Machine 不只描述「現在可能在哪個 State」,也會明確定義:
接下來允許往哪個 State 走。
但到底是什麼事情,讓這條 Transition 開始發生的呢?
把其中一條箭頭放大來看,就會看到下一個角色。

這裡把剛剛其中一條 ② Transition 單獨放大。
這次要看的,是箭頭上面的 ④ coin。
它代表 Event / Trigger(事件/觸發),也就是:
發生了什麼事情,讓系統開始判斷這一次 State Transition。
在這個例子裡,coin 就是「投幣」。
所以整條規則可以讀成:
當閘門目前處於
Locked,發生coinEvent 時,可以 Transition 到Unlocked。
這裡可以先把兩個角色分清楚:
Transition
→ State 怎麼改變
Event / Trigger
→ 是什麼事情觸發這次轉換判斷
所以 coin 不是一個 State,而是一件發生的事情。
不過 Event 發生,也不代表一定能進到下一個 State。
有時候系統還要再確認:
目前的條件,真的允許這條 Transition 嗎?
這就會進到下一個角色:Guard / Condition(守衛條件/轉換條件)。
前面看到 Event 會觸發一次 Transition 判斷。
但實際系統裡,事情發生了,不代表這條 Transition 就一定能往下走。
這次換一個簡單的文件審核情境來看。

圖上的 ④ approve 是剛剛認識的 Event / Trigger(事件/觸發)。
這次真正要看的,是後面的 ⑤ [檢查通過]。
它代表 Guard / Condition(守衛條件/轉換條件),用來判斷:
目前是否符合這條 Transition 可以發生的條件?
這裡的「條件」很重要。
因為放進程式裡,它通常會被判斷成 true 或 false,也就是一個 Boolean condition(布林條件)。。
例如圖中的 [檢查通過],可以先理解成:
檢查通過 = true
→ 允許 Transition
檢查通過 = false
→ 不執行這條 Transition
所以這條規則完整讀起來就是:
當文件目前處於
In Review,發生approveEvent,而且[檢查通過] = true,才可以 Transition 到Published。
也就是:
Event
→ 發生了什麼事情?
Guard
→ 條件是否成立?
Event 負責觸發判斷,Guard 則決定這條 Transition 現在到底能不能走。
接下來,就可以把前面認識的 State、Transition、Event 和 Guard,放回同一條規則裡一起看。
現在 State、Transition、Event、Guard 都有了。
可以把一條最基本的狀態轉換整理成:
Current State
目前狀態
+
Event / Trigger
事件/觸發
+
Guard / Condition(如果有)
守衛條件
↓
Transition
狀態轉換
↓
Next State
下一個狀態
以剛剛的文件審核情境來看:
目前 State = In Review
Event = approve
Guard = 檢查通過?
當 [檢查通過] = true:
In Review 才能 Transition 到 Published。
到這裡,一張 State Diagram 就不只是「幾個方框加上幾條箭頭」。
一條 Transition 背後,其實是在表達:
現在在哪個 State、發生了什麼,以及在需要條件判斷時,現在到底允不允許改變 State。
而如果不只看眼前這一條 Transition,把整套 State 從頭到尾重新拉遠,就會看到另一個概念:
Lifecycle(生命週期)。
前面一路拆開了 State、Transition、Event 和 Guard。
如果把這些狀態變化重新串成一整段來看,就是 Lifecycle(生命週期)。
Lifecycle 描述的是:
一個系統或物件,從開始之後,可能經過哪些 State 與 Transition 的整段狀態歷程。
例如一份簡化的文件流程,可以畫成:
●
↓
Draft
↓
In Review
↓
Published
↓
◎
最上面的 ●,代表 Initial State(起始狀態)的入口。
它表示這套 State Machine 一開始會先進入哪個 State。
最下面的 ◎,則代表 Final State(最終狀態/終態),表示這段 Lifecycle 在這裡完成。
所以可以先這樣理解:
Initial State
→ 從哪裡開始
Lifecycle
→ 中間可能經過哪些 State 與 Transition
Final State
→ 如果有明確終點,在哪裡結束
不過不是每個 State Machine 都一定有 Final State。
像前面的閘門例子,就可以一直在 Locked 和 Unlocked 之間反覆轉換,本身沒有明確終點。
所以 Lifecycle 的重點不是「一定要從 Initial 走到 Final」,而是:
把同一個系統一路可能經過的 State 與 Transition,當成一整段歷程來看。
看到 State Diagram 的方框和箭頭,很容易讓我想到以前也看過的:
Flowchart(流程圖) 和 User Flow(使用者流程)。
三種圖看起來都可能是方框加箭頭,但真正關心的問題不一樣。
| User Flow(使用者流程) | Flowchart(流程圖) | State Machine(狀態機) | |
|---|---|---|---|
| 主要視角 | 使用者 | 流程/程序 | 系統/物件 |
| 核心問題 | 使用者怎麼一步一步完成一件事? | 這段流程接下來要執行哪一步? | 現在在哪個 State?允許怎麼改變? |
| 常關注 | 操作、頁面、步驟、分支 | 處理步驟、判斷、分支 | State、Transition、Event、Guard |
所以最簡單可以先這樣記:
User Flow 看「人怎麼走」。
Flowchart 看「程序怎麼跑」。
State Machine 看「系統現在在哪個 State,又允許怎麼變」。
而且三種圖不是互相取代。
同一個功能裡,使用者可能有一條操作路徑,系統也可能有一段自己的處理流程,同時某個物件又有自己的狀態變化。
只是它們拿來回答的問題不同。
那今天的 State Machine 就先教到這裡啦!
我自己會這麼喜歡 State Machine,不是因為它可以把流程畫得比較漂亮。
而是 BuJo 開發到後期的時候,四種活動模式、不同截止時間、各種邊界情況混在一起,我腦袋真的常常打結啊~~~
而且團隊討論時,很常牛頭不對馬嘴?
但把 State Machine 畫出來之後,原本散在不同地方的判斷,終於被攤在同一張圖上。
大家也比較能直接指著同一張圖,確認彼此想的是不是同一套流程。
它甚至還真的幫我發現過 BuJo 裡 deadline_at 和 vote_deadline_at 的邏輯問題!
今天我們把 State Machine 的圖看懂了。
明天就直接回到 BuJo,用實際的 Code 看看這些 State、Transition、Event、Guard 到底怎麼變成程式裡的規則吧~