還記得我們在 Day 09、Day 10 有 2 個自製版本的 createMacine
[Day 09] 的 createMacine
是 Pure function - 無狀態版,要自己另外儲存 previousState
, nextState
[Day 10] 的 createMacine
是 有狀態版,會回傳我們一個 machine 的 object instance ,裡面記憶著當前的狀態(currentState
)。
Day 16 介紹了第一個 XState API createMacine
,也是無狀態 Pure function 版的 stateMachine 。
const doorMachine = createMachine(machineConfig);
const state0 = doorMachine.initialState;
console.log(state0);
const state1 = doorMachine.transition(state0, "開門");
console.log(state1);
const state2 = doorMachine.transition(state1, "關門");
console.log(state2);
今天要來介紹的就是第二種: 有狀態版
第一版 Pure Function 固然美好,純函數、有彈性、可測試...,但現實世界的應用場景
所以我們這才需要第二種版本,可儲存狀態的狀態機,也就是當初 Day 10 我們的小練習採用一個 object 實作,而 XState 官方在 interpret
API這裡的描述提到了 Interpreter、Instance、Service,我也想在這邊嘗試著解釋,見以下原文描述
The interpreter is responsible for interpreting the state machine/statechart and doing all of the above - that is, parsing and executing it in a runtime environment. An interpreted, running instance of a statechart is called a service. by XState
Interpreter 中文翻譯有 直譯器、同步口譯、表演者、演繹者,當初似乎沒能找到適合的翻譯,所以一開始這段沒能理解很透徹
這邊嘗試用 Wiki 中的 Interpreter解釋
In computer science, an interpreter is a "computer program" that directly executes instructions written in a programming or scripting language, without requiring them previously to have been compiled into a machine language program. An interpreter generally uses one of the following strategies for program execution:
- Parse the source code and perform its behavior directly;
// 剖析原始碼、直接執行原始碼的行為
- Translate source code into some efficient intermediate representation or object code and immediately execute that;
// 將原始碼(人類讀得懂的程式碼)翻譯成更高效的中介程式碼、目的碼(機器讀得懂的程式碼、CPU 可直接解讀的、二進位檔案),並立即執行程式碼
- Explicitly execute stored precompiled code[1] made by a compiler which is part of the interpreter system.
// 明確執行已經預先被儲存、被 Compiler 預先編譯過的程式碼,這 compiler 是來自於某個 interpreter system 的一部分。
by Wiki - Interpreter
這邊看完似乎仍一頭霧水,但 Ken 認為...
我認爲 XState 文件提及的 interpreter API,比較接近 Wiki 的第 1 點解釋,可被執行地、已經被預存的一些程式碼(我們傳入了 Machine Config )被 XState interpreter API 解析好,我們就可以在 Runtime 執行我們所需要的這些 狀態轉換、Side Effect 執行、與外部溝通等等,以及如 Wiki 第一行,an interpreter is a "computer program"... ,這 Interpreter,就像另外一個程式,幫我們執行特別的任務。
An interpreted, running instance of a statechart is called a service. by XState
Instance 一詞,在 OOP 中相當常見、相對容易理解
程式開發中,我們常用物件模擬現實世界的各種物體等等,以汽車舉例:
汽車有各種特點、屬性(如:廠牌、顏色、製造年份)及各種行為(加速前進、後退、轉彎等),我們可以透過 class 這個模板、像工廠一般。我們只要發訂單給工廠(car class)說我要製作一台 2022 玫瑰金 特斯拉,這個汽車 class 就可以幫我製作出一台我的特斯拉(car object)。而我這台可實際被執行、被我開上路的特斯拉就被稱為實體(Instance),
如果我的描述不夠清楚,可以另外參考
什麼是物件導向(2):Object, Class, Instance
An interpreted, running instance of a statechart is called a service. by XState
Service 一詞,在學習 DI 時也很容易看見,以 Angular 官網舉例
服務是一個廣義的概念,它包括應用所需的任何值、函式或特性。狹義的服務是一個明確定義了用途的類別。它應該做一些具體的事,並做好。
Service is a broad category encompassing any value, function, or feature that an app needs. A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. by Angular TW
Service 在 OOP 也可想成是那些形塑 商業邏輯 、可執行商業邏輯行為(behavior)的物件 (object)
by stack overflow - What is the service class in programming?
A service is an interpreted machine; i.e., an actor that represents a machine. by XState - Glossary
這邊還有提到 Actor 由於篇幅的關係,有機會再開另外一篇解釋
Actor 簡言之,就是一個可擁有自己內部狀態、並且與外部互動溝通的一個小程式碼單位
Actor model 也是另外一種數學運算模型,我們這邊先知道 每個小 State Machine Service or Instance 就是一個 Actor 即可。
The interpreter is responsible for interpreting the state machine/statechart and doing all of the above - that is, parsing and executing it in a runtime environment. An interpreted, running instance of a statechart is called a service. by XState
這段文字也是我當初在閱讀官方文件時,一頭霧水、無感,需要補齊許多知識脈絡的地方,希望透過上面的解釋,能讀者們更理解,之前提到的這一段英文描述的意思。
所以說 Machine Config 是一個充滿我們商業邏輯的設定,根據這個商業邏輯所生產的 object instance 我們也把它稱為是一個 service。
關於本篇的描述,若有解釋不佳或不適當的地方,也請大家多多指教、謝謝!
明天我們將繼續帶大家實際操作 XState interpret
API
https://xstate.js.org/docs/guides/interpretation.html#interpreting-machines
https://xstate.js.org/docs/about/glossary.html#service
https://stackoverflow.com/questions/29715105/what-is-the-service-class-in-programming
就我非常粗淺的理解,要操作一個 state machine,直接操作或是透過 interpreter 操作都可以,不過使用 interpreter 可以幫助我們建立 instance,也就是說
const serviceA = interpret(doorMachine).onTransition(state => ...);
const serviceB = interpret(doorMachine).onTransition(state => ...);
serviceA 和 serviceB 會是操作兩個不同的實體機器。好處是我們不需要重複撰寫 machine config。另外我猜測 interpreter 也提供了一些包裝好的方法,讓我們能夠更輕易的操作 state machine
不知道這樣的理解是不是正確
沒錯,作為 instance 能讓我們記憶狀態、操作方法,而 service 更釋出許多好用的方法,讓我們能輕易處理商業邏輯的操作。
也就是第一段的
- 需要持續追蹤當前的 state ,並且保存它!(浪費這麼多變數儲存似乎不妥?)
- 需要執行 Side Effects (包含操控 I/O、Call API、寫入檔案、印出螢幕...)
- 延遲處理 or 處理延遲的 轉移 (Transition) 及 事件 (Event)
- 跟外部的服務(Service)溝通
我覺得我這篇的敘述、描述好像太著重在拆解官網每個句子、單字的翻譯跟轉換,反而沒有傳達好整體思想的原貌跟主要概念。
應該找時間回來調整一下