前回は状態機械のモデル定義を行い、そのままKaleidoxで動作させることができることを紹介しました。
前回の状態機械はステートがフラットに並んでいる単純なものでしたが、実際のアプリケーションを作成するにはやや不便なところがあります。
というのは、実際のアプリケーションではキャンセルや保留といった状態遷移が必須となってきますが、フラットなステートの遷移では表現が煩雑になったり、実現が困難になったりという問題があるからです。
この問題を解決する仕組みの一つがサブステートです。
今回はこのサブステートをつかってキャンセル機能を実現します。
モデル
前回のモデルに対して以下の拡張を行っています。
- Cancelイベントを追加
- 基幹部をサブステートとして、サブステート内の任意のステートでキャンセル可能にした
この拡張設定を行った状態機械の定義は以下になります。
* event event=[{ name="confirm" },{ name="reject" },{ name="delivered" },{ name="cancel" }] * statemachine statemachine={ name="purchase" state=[{ name=init transition=[{ to=running }] },{ name=canceled transition=[{ to=FINAL }] }] statemachine=[{ name="running" state=[{ name=applying transition=[{ to=confirming }] },{ name=confirming transition=[{ guard=confirm to=confirmed },{ guard=reject to=rejected }] },{ name=confirmed transition=[{ to=delivering }] },{ name=rejected transition=[{ to=FINAL }] },{ name=delivering transition=[{ guard=delivered to=delivered }] },{ name=delivered transition=[{ to=FINAL }] }] transition=[{ guard=cancel to=canceled }] }] }
イベント
以下の3つのイベントを定義しています。
- confirm
- 確認OK
- reject
- 確認却下
- delivered
- 配送済み
- cancel
- キャンセル
前回のものからはcancelイベントを追加しています。
状態機械
定義したモデルを状態機械図で記述すると以下になります。
サブステートrunningを作成し、applying, confirming, confirmed, rejected, delivering, deliveredの各ステートをサブステートrunningに移しました。
また、新たにキャンセル状態を示すステートcanceledを作成し、サブステートrunningの任意のステートからの状態遷移ができるようにしました。
実行
それでは実行してみます。
まず正常系とrejectイベントの2つのルートを確認します。これは前回と同じ動きになります。
この後に、今回のテーマであるサブステートを利用したcancelイベントの振る舞いを確認します。
正常系
最初に正常系の動きです。前回の正常系と同じ動きになります。
まずstatemachine-new関数で状態機械を生成します。生成する状態機械のモデル名はpurchaseです。
生成した状態機械をsetq関数で変数smに束縛します。
状態機械の名前はpurchaseです。状態機械の状態はconfirmingになっています。
kaleidox> statemachine-new 'purchase StateMachine[purchase.running.confirming] kaleidox> setq sm StateMachine[purchase.running.confirming]
状態機械purchaseを作成すると、すぐにサブステートrunning内のステートconfirmingに遷移します。
次にevent-issue関数でconfirmイベントを発行します。
confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。引き続きサブステートrunning内です。
kaleidox> event-issue 'confirm Event[confirm] kaleidox> sm StateMachine[purchase:delivering]
次にevent-issue関数でconfirmイベントを発行します。
confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。引き続きサブステートrunning内です。
kaleidox> event-issue 'delivered Event[delivered] kaleidox> sm StateMachine[purchase:FINAL]
これで状態機械の状態遷移は終了です。
reject
続けてrejectイベントのルートも確認しておきます。
statemachine-new関数で状態機械purchaseを生成した直後はconfirming状態になっています。
ここでrejectイベントを発行するとモデルの定義どおり最終状態FINALに遷移しました。
kaleidox> statemachine-new 'purchase StateMachine[purchase.running.confirming] kaleidox> setq sm StateMachine[purchase.running.confirming] kaleidox> event-issue 'reject Event[reject] kaleidox> sm StateMachine[purchase:FINAL]
状態機械purchaseはrejectイベントを受信することで状態rejectedに遷移しますが、状態rejectedから最終状態FINALの間にガードがないので最終状態FINALに自動遷移しています。
cancel
次はcancelイベントです。サブステートrunning内の任意のステートでcancelが可能になります。
まずステートconfirmingでcancelイベントを出してみます。以下に示すとおりFINAL状態になりました。状態機械がcancelイベントを受信した結果、ステートcanceledに遷移し、ステートcanceledにはガードを設定していないのでFINAL状態に遷移しています。
kaleidox> statemachine-new 'purchase StateMachine[purchase.running.confirming] kaleidox> setq sm StateMachine[purchase.running.confirming] kaleidox> event-issue 'cancel Event[cancel] kaleidox> sm StateMachine[purchase:FINAL]
次にステートdeliveringでcancelイベントを出してみます。以下に示すとおりこちらもFINAL状態になりました。
kaleidox> statemachine-new 'purchase StateMachine[purchase.running.confirming] kaleidox> setq sm StateMachine[purchase.running.confirming] kaleidox> event-issue 'confirm Event[confirm] kaleidox> sm StateMachine[purchase.running.delivering] kaleidox> event-issue 'cancel Event[cancel] kaleidox> sm StateMachine[purchase:FINAL]
いずれの場合もサブステートrunninigに設定した以下の遷移によって、サブステート内の状態からcanceled状態に遷移することができました。
transition=[{ guard=cancel to=canceled }]
まとめ
サブステートをもった状態機械モデルでキャンセル機能をモデル定義し、Kaleidox上で動作させてみました。
キャンセル機能は実際のアプリケーションでも頻出の機能ですが、実装するのは案外煩雑なので、サブステートを用いたシンプルなモデル定義のみでそのまま動作するのは大きなメリットだと思います。
サブステートを使った別の頻出機能として保留機能があります。次回は状態機械モデルとKaleidoxで保留機能を実現する予定です。
諸元
- Kaleidox
- 0.3.0