區分狀態和數據,設計出簡潔高效的狀態機

閱讀時間約 11 分鐘


情境:有一個變數A的初始值是 undefined,當經過 http request 後,若是失敗則需要重試,若是重試3次失敗則通知重試失敗錯誤訊息,須 request 成功後才賦予變數 A 的值為 0 或 1。


問題是,變數 A 有三種可能性,應該將其當作狀態嗎?


一個簡單的狀態機範例

先透過 xstate.js 來描述這樣的狀態機,我們可以建立一個狀態機,其中包括初始狀態、重試狀態、成功狀態和失敗狀態。以下是一個簡單的範例:


fetch 狀態機 (Generated by https://stately.ai/viz)

fetch 狀態機 (Generated by https://stately.ai/viz)


import { createMachine, interpret } from 'xstate';

// 定義狀態機
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: {
retries: 0,
A: undefined,
},
states: {
idle: {
on: {
FETCH: 'loading'
}
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: 'setData'
},
onError: {
target: 'retry',
actions: 'incrementRetries'
}
}
},
retry: {
always: [
{
target: 'failure',
cond: 'maxRetriesReached'
},
{ target: 'loading' }
]
},
success: {
type: 'final'
},
failure: {
type: 'final'
}
}
}, {
actions: {
incrementRetries: (context) => context.retries++,
setData: (context, event) => context.A = event.data
},
guards: {
maxRetriesReached: (context) => context.retries >= 3
}
});

// 模擬 fetchData 函數
const fetchData = () => {
return new Promise((resolve, reject) => {
// 模擬 HTTP request
setTimeout(() => {
// 假設 50% 的機率成功,50% 的機率失敗
Math.random() > 0.5 ? resolve(Math.random() > 0.5 ? 1 : 0) : reject('Failed');
}, 1000);
});
};

// 創建服務
const fetchService = interpret(fetchMachine.withConfig({
services: {
fetchData: fetchData
}
})).onTransition(state => {
console.log(state.value, state.context);
});

// 啟動服務
fetchService.start();

// 發送 FETCH 事件開始請求
fetchService.send('FETCH');


這段代碼建立了一個狀態機,其邏輯如下:

  1. 初始狀態是 idle,接收到 FETCH 事件後進入 loading 狀態。
  2. loading 狀態中,執行 fetchData 請求,請求成功則進入 success 狀態,並將變數 A 設置為 0 或 1;請求失敗則進入 retry 狀態,並增加重試次數。
  3. retry 狀態中,如果重試次數達到 3 次,則進入 failure 狀態,否則返回 loading 狀態重新嘗試。
  4. successfailure 狀態是終結狀態,表示狀態機的運行結束。


如何辨別是狀態還是數據?

從上述例子可以看出變數 A 其實並不適合作為狀態機中的狀態。

在設計狀態機時,辨別是狀態還是數據是一個關鍵問題。以下是一些指導原則,可以幫助你做出這個區分:

狀態的特徵

  1. 行為驅動: 狀態反映系統在特定時間點上的行為或狀態。不同的狀態導致系統有不同的行為方式。例如,“加載中”、“重試中”、“成功”和“失敗”這些狀態會導致系統採取不同的行為。
  2. 有限且明確: 狀態的數量通常是有限的,並且在設計時可以明確列出來。每個狀態都有明確的開始和結束條件。
  3. 可觀察和區分: 每個狀態都是系統中的一個獨立的狀態,應該是可以觀察和區分的。你應該能夠清楚地描述在某個狀態下系統的行為特徵。

數據的特徵

  1. 信息驅動: 數據是系統運行所需的具體信息或變量。數據用來記錄狀態機運行過程中的細節和參數,比如計數器、標識符、返回的數據等。
  2. 可變性和靈活性: 數據可以是變化的,可能有無限多個值。數據的值通常是動態改變的,並且它們的變化並不會改變狀態機的結構,只是影響其內部邏輯。
  3. 共享和獨立: 數據可以在多個狀態之間共享和使用,而狀態則不能在其他狀態中直接引用。數據是運行時期的屬性,而狀態是行為時期的屬性。

在這個例子中:

  • 狀態:
    • idle: 系統閒置等待請求。
    • loading: 系統正在發送請求。
    • retry: 系統正在處理重試邏輯。
    • success: 請求成功。
    • failure: 請求失敗。
  • 數據:
    • retries: 記錄重試的次數,是一個可變的計數器。
    • A: 請求返回的數據,是請求結果的具體信息。


補充:一些狀態機設計原則

設計良好的狀態機能夠使系統的行為更加清晰和易於管理。以下是一些設計原則,可以幫助你明確識別和定義狀態:

1. 單一責任原則 (Single Responsibility Principle)

每個狀態應該有明確的單一責任,即它應該只負責一個特定的行為或任務。如果某個狀態需要負責多個不同的行為,考慮將其拆分成多個獨立的狀態。

2. 明確的狀態轉移 (Clear State Transitions)

每個狀態之間的轉移應該是明確的和有意圖的。應避免不必要的狀態轉移,並確保每個轉移都有合理的觸發條件。

3. 有限狀態原則 (Finite State Principle)

狀態機應該只包含有限的狀態。過多的狀態會增加系統的複雜性,難以管理和維護。應盡量保持狀態數量在合理範圍內。

4. 原子狀態 (Atomic States)

狀態應該是原子的,即在任何時刻系統應該只處於一個明確的狀態中。避免狀態重疊或多重狀態,這會導致系統行為不確定。

5. 行為一致性 (Behavior Consistency)

在每個狀態中,系統的行為應該是一致的。這意味著在特定狀態下系統應該總是執行相同的操作,並對相同的事件做出一致的反應。

6. 狀態與數據分離 (Separation of State and Data)

狀態應該描述系統的行為,而數據(如 context)應該描述狀態機運行時的數據。這有助於保持狀態定義的清晰和簡潔,並使數據管理更加靈活。

7. 可觀察性 (Observability)

確保每個狀態和狀態轉移都是可觀察的,即可以在運行時監控和記錄狀態機的行為。這對於調試和監控系統運行非常重要。

8. 可測試性 (Testability)

狀態應該是可測試的。設計狀態機時應考慮如何對每個狀態和狀態轉移進行單元測試,以確保系統行為符合預期。

9. 簡單性 (Simplicity)

保持狀態機的設計簡單。避免過於複雜的狀態結構和不必要的狀態轉移。簡單的狀態機更容易理解和維護。

10. 具體案例分析 (Case Analysis)

通過具體的業務需求和使用案例來分析系統需要哪些狀態。對每個狀態進行詳細分析,確保它們能夠覆蓋所有業務場景。

例子:HTTP Request 狀態機

以下是上述原則在 HTTP Request 狀態機中的應用示例:

  1. 單一責任:
    • loading 狀態負責處理 HTTP 請求。
    • retry 狀態負責處理重試邏輯。
    • success 狀態表示請求成功。
    • failure 狀態表示請求失敗。
  2. 明確的狀態轉移:
    • idle -> loading (接收到 FETCH 事件)
    • loading -> success (請求成功)
    • loading -> retry (請求失敗)
    • retry -> loading (重試次數未達到限制)
    • retry -> failure (重試次數達到限制)
  3. 有限狀態:
    • 狀態數量控制在合理範圍內 (idle, loading, retry, success, failure)
  4. 原子狀態:
    • 在任何時刻,系統只會處於上述五個狀態之一。
  5. 行為一致性:
    • loading 狀態中,系統總是嘗試發送 HTTP 請求。
  6. 狀態與數據分離:
    • 重試次數和請求結果數據保存在 context 中,而不是狀態中。
  7. 可觀察性:
    • 每次狀態轉移時,記錄當前狀態和上下文數據。
  8. 可測試性:
    • 為每個狀態和轉移條件編寫單元測試,確保系統行為符合預期。
  9. 簡單性:
    • 保持狀態機設計簡單,易於理解和維護。
  10. 具體案例分析:
  • 通過分析 HTTP 請求的不同場景,確定需要哪些狀態來處理這些場景。

通過應用這些設計原則,可以有效地識別和定義狀態,從而構建出清晰且可維護的狀態機。


總結

  1. 判斷行為還是信息: 如果某個項目改變了系統的行為,那麼它更可能是一個狀態。如果它只是提供了執行這些行為所需的信息,那麼它更可能是數據。
  2. 數量和範圍: 狀態的數量通常是有限且可枚舉的,而數據的範圍通常是無限的。
  3. 共享和獨立: 數據可以在多個狀態中共享,而狀態是彼此獨立且不共享的。

通過這些指導原則,你可以更清晰地區分狀態和數據,並設計出更簡潔和高效的狀態機。

1會員
7內容數
請尋找快樂的動力,換個觀點,保持對世界的好奇與熱情。
留言0
查看全部
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
​熊パン手感烘焙堅持採用好原料製作麵包,每日不同時段限量供應不同種類麵包。早上十時出爐各式料理鹹麵包、下午一時到五時半出爐各式麵包、歐式麵包、吐司。這是一家堅持職人手感店家,獨立經營店家在台灣烘培市場中也吸引許多潛在顧客存在。 熊パン手感烘焙相關資訊:: ​地址: 新北市市汐止區福德一路298
Thumbnail
上週包裝工在外卡輪驚奇的整場壓制牛仔隊後,本周將面對國聯第一的舊金山四九人,NFL官方Youtube頻道做的預告影片中,沒有人預測包裝工會勝出,可見兩隊在賽季時的表現是天壤之別。
Thumbnail
狗狗的咳嗽(coughing) ,噴嚏(sneezing),逆向噴嚏(reverse sneezing),作嘔(gagging) 等是我們在每天門診中常常都會看到的問題, 這兩個症狀大多飼主都可以描述正確, 但對於逆向噴嚏, 作嘔(gagging )乾嘔 (retching)就常常聽到五花八門的
你有聽過DIKW金字塔嗎? DIKW金字塔,給出了「數據,資訊,知識,智慧」之間的結構與功能關係。 但如果是我來定義DIKW四元素,會是
Thumbnail
10月1日清晨一路從重陽路,邊拍邊看來到了位於經貿二路168-188號的「中國信託金融園區」,發現10月一到,整個金融園區的建築,已經悄悄地披上了萬聖節群魔亂舞的人物圖案,不過別擔心嚇到小朋友,因為所有的人物造型設計,都是走可愛風,讓人感受到繽紛多彩充滿魔幻的氛圍。從入口的柱子就能看到多種不同人物造
Thumbnail
在台灣投資債券基金的人應該為數不少,對於債券基金的名稱,包括投資等級債、高收益債、垃圾債等等,你了解多少呢?哪些適合你呢? 區分這些債券最重要的一個關鍵就是信用評等(Credit Rating),以下來我們就來聊聊債券的信用評等!
Thumbnail
為什麼認識士族名莊很重要 士族名莊 (Cru Bourgeois) 的由來 1932 年:中級酒莊 (Cru Bourgeois) 分級制度首次擬定並確立,但當時還沒有被官方機構認可。當時選出了 444 家酒莊,其中有 6 間特優,100 間優等,剩下 338 間一般的士族名莊。 結論
Thumbnail
1855 年波爾多分級制度有兩個重點:1. 這個分級列表評級的不是葡萄園,而是酒莊。2. 當時的評級標準不是葡萄酒當年份的品質,而是當時的貿易價格。這和法國另外一個重要產區勃根地重視地塊,重視風土表現的評級標準有很根本的差異。這樣南轅北轍的文化也和這兩個地區發展葡萄酒產業的歷史差異,有很大的關係。
Thumbnail
✔ 認為自己只是不喜歡某件事 ✔ 但其實沒有真正嘗試過 ✔ 而且嘗試的機會不斷出現 👉 可能就是假性厭惡 前一陣子我因為準備塔羅影音課程而挖掘到我內心深處「對外表沒自信的恐懼」的恐懼,居然偽裝成「我只是討厭亮相」。原來我不是真的討厭拍照拍片,而是害怕拍照拍片而長期排斥、抗拒所導致的「假性厭惡」。
Thumbnail
文、圖/奧美整合行銷傳播集團提供   WPP集團宣布,由奧美集團大中華區副董事長莊淑芬(Shenan Chuang)出任該集團新設置之「WPP集團台灣區董事長」一職,同時繼續身兼原有職務。   針對這項重要任命,WPP集團創辦人兼執行長蘇銘天爵士(Sir Martin Sorrell)表示,台灣
Thumbnail
接下來第二部分我們持續討論美國總統大選如何佈局, 以及選前一週到年底的操作策略建議 分析兩位候選人政策利多/ 利空的板塊和股票
Thumbnail
🤔為什麼團長的能力是死亡筆記本? 🤔為什麼像是死亡筆記本呢? 🤨作者巧思-讓妮翁死亡合理的幾個伏筆
Thumbnail
​熊パン手感烘焙堅持採用好原料製作麵包,每日不同時段限量供應不同種類麵包。早上十時出爐各式料理鹹麵包、下午一時到五時半出爐各式麵包、歐式麵包、吐司。這是一家堅持職人手感店家,獨立經營店家在台灣烘培市場中也吸引許多潛在顧客存在。 熊パン手感烘焙相關資訊:: ​地址: 新北市市汐止區福德一路298
Thumbnail
上週包裝工在外卡輪驚奇的整場壓制牛仔隊後,本周將面對國聯第一的舊金山四九人,NFL官方Youtube頻道做的預告影片中,沒有人預測包裝工會勝出,可見兩隊在賽季時的表現是天壤之別。
Thumbnail
狗狗的咳嗽(coughing) ,噴嚏(sneezing),逆向噴嚏(reverse sneezing),作嘔(gagging) 等是我們在每天門診中常常都會看到的問題, 這兩個症狀大多飼主都可以描述正確, 但對於逆向噴嚏, 作嘔(gagging )乾嘔 (retching)就常常聽到五花八門的
你有聽過DIKW金字塔嗎? DIKW金字塔,給出了「數據,資訊,知識,智慧」之間的結構與功能關係。 但如果是我來定義DIKW四元素,會是
Thumbnail
10月1日清晨一路從重陽路,邊拍邊看來到了位於經貿二路168-188號的「中國信託金融園區」,發現10月一到,整個金融園區的建築,已經悄悄地披上了萬聖節群魔亂舞的人物圖案,不過別擔心嚇到小朋友,因為所有的人物造型設計,都是走可愛風,讓人感受到繽紛多彩充滿魔幻的氛圍。從入口的柱子就能看到多種不同人物造
Thumbnail
在台灣投資債券基金的人應該為數不少,對於債券基金的名稱,包括投資等級債、高收益債、垃圾債等等,你了解多少呢?哪些適合你呢? 區分這些債券最重要的一個關鍵就是信用評等(Credit Rating),以下來我們就來聊聊債券的信用評等!
Thumbnail
為什麼認識士族名莊很重要 士族名莊 (Cru Bourgeois) 的由來 1932 年:中級酒莊 (Cru Bourgeois) 分級制度首次擬定並確立,但當時還沒有被官方機構認可。當時選出了 444 家酒莊,其中有 6 間特優,100 間優等,剩下 338 間一般的士族名莊。 結論
Thumbnail
1855 年波爾多分級制度有兩個重點:1. 這個分級列表評級的不是葡萄園,而是酒莊。2. 當時的評級標準不是葡萄酒當年份的品質,而是當時的貿易價格。這和法國另外一個重要產區勃根地重視地塊,重視風土表現的評級標準有很根本的差異。這樣南轅北轍的文化也和這兩個地區發展葡萄酒產業的歷史差異,有很大的關係。
Thumbnail
✔ 認為自己只是不喜歡某件事 ✔ 但其實沒有真正嘗試過 ✔ 而且嘗試的機會不斷出現 👉 可能就是假性厭惡 前一陣子我因為準備塔羅影音課程而挖掘到我內心深處「對外表沒自信的恐懼」的恐懼,居然偽裝成「我只是討厭亮相」。原來我不是真的討厭拍照拍片,而是害怕拍照拍片而長期排斥、抗拒所導致的「假性厭惡」。
Thumbnail
文、圖/奧美整合行銷傳播集團提供   WPP集團宣布,由奧美集團大中華區副董事長莊淑芬(Shenan Chuang)出任該集團新設置之「WPP集團台灣區董事長」一職,同時繼續身兼原有職務。   針對這項重要任命,WPP集團創辦人兼執行長蘇銘天爵士(Sir Martin Sorrell)表示,台灣