2021 年 01 月 === ### 蒼時 ## 狀態機 通常就是指「有限狀態機(FSM)」在現實中大多數的情況也是屬於有限狀態機,簡單來說就是從狀態 A 切換到 B 的時候需要滿足條件,否則無法切換。 * 狀態:物件當下的狀態 * 事件:觸發改變狀態的原因 * 過度:改變狀態的過程 * 過度條件:限制狀態改變的條件 狀態機在很多地方都會被使用到,像是遊戲中的「動畫控制」就是其中一項。 > 準備了簡單的動畫控制範例跟大家分享 ### 案例 門有「開啟」跟「關閉」兩種狀態,當我們「開門」的時候會進入「開門中」的過度,而要進入這個過渡的條件是「門關閉著」的狀態。 維基百科:[有限狀態機](https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA) ## 有向無環圖(Directed Acyclic Graph) 跟狀態機類似的是 DAG 我們可以順便看一下,跟前面狀態機的圖相比起來 DAG 並不會構成「循環」也就是最終一定會在某個狀態下終止。 舉例來說,一個電商網站的訂單狀態的變化就可以用 DAG 來表示,從產生訂單到付款、完成訂單即時中間可能會因為有「失敗狀態」構成循環,但最終一定會抵達某個終點。 維基百科:[有向無環圖](https://zh.wikipedia.org/wiki/%E6%9C%89%E5%90%91%E6%97%A0%E7%8E%AF%E5%9B%BE) ## AASM AASM 是 Ruby 的狀態機實作套件,同時也支援 ActiveRecord 在使用上基本上就是對應狀態機的設計,因此我們在使用 AASM 的時候同樣會定義以下資訊。 * 狀態 `state :idle` * 事件 `event :walk` * 過度 `transition from: :idle, to: :walking` ### Callback 單純的狀態機在大多數時候是不太夠用的,因此 AASM 提供了 Callback 在不同的情況下提供可以做額外處理的機會。 舉例來說 `state`(狀態)有 `exit` 和 `enter` 兩種類型事件,在從狀態 A 切換到 B 時會先呼叫狀態 A 的 `before_exit` 和 `exit` 等 Callback 來處理,而進入狀態 B 時則會呼叫 `before_enter` 和 `enter` 等 Callback 來進行特定的動作。 > 在 AASM 的設計裡面狀態、事件、過度都有預留 Callback 可以使用,呼叫的順序則需要參考網站上的列表。 ### Guard 正常的情況下「過度條件」通常是前一個狀態符合要求,但某些時候我們會需要跟物件其他資訊一起判斷,也因此 AASM 提供了 Guard 的選項讓我們可以針對這類情況客製化。 ### Binding Event > 沒用過 😂 簡單說當物件有多種狀態時,可以連動另一個狀態事件一起被觸發。 ### ActiveRecord 支援 在 Callback 的部分中增加了像是`after_commit` 可以在資料庫操作中被 Commit 後才呼叫(例如處理完畢後呼叫另一個 Job 接著處理)除此之外還有像是悲觀鎖(Pessimistic Locking)的機制可以打開,通常是為了確保資料再變更時不會遇到像是 Race Condition 而造成預期外的影響。