前回は状態機械のサブステートを用いてキャンセル機能を実現しました。
サブステートに並ぶ状態機械の重要な機能にヒストリーがあります。
ヒストリーは状態間の遷移時に、遷移元状態の履歴を保持しておく機能で、状態遷移後に元の状態に戻る遷移を行うことができます。
ヒストリーを用いることで、保留/再開機能を記述することができます。今回はこのヒストリーを使ってKaleidoxの状態機械で保留/再開機能を実現します。
モデル
前回のモデルに対して以下の拡張を行っています。
- suspendイベント、resumeイベントを追加
- 保留状態を示すsuspendedステートを追加
- サブステートrunningの任意のステートでsuspendイベントにより保留可能にした
この拡張設定を行った状態機械の定義は以下になります。
* event event=[{ name="confirm" },{ name="reject" },{ name="delivered" },{ name="cancel" },{ name="suspend" },{ name="resume" }] * statemachine statemachine={ name="purchase" state=[{ name=INIT transition=[{ to=running }] },{ name=canceled transition=[{ to=FINAL }] },{ name=suspended transition=[{ guard=resume to=HISTORY }] }] 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 },{ guard=suspend to=suspended }] }] }
イベント
以下の6つのイベントを定義しています。
- confirm
- 確認OK
- reject
- 確認却下
- delivered
- 配送済み
- cancel
- キャンセル
- suspend
- 保留
- resume
- 再開
前回のものからはsuspendイベントとresumeイベントを追加しています。
状態機械
定義したモデルを状態機械図で記述すると以下になります。
前回、サブステートrunningを作成し、applying, confirming, confirmed, rejected, delivering, deliveredの各ステートを定義しています。
以下の定義により新たに保留状態を示すステートsuspendedを作成し、サブステートrunningの任意のステートからの状態遷移ができるようにしました。ステートsuspendedからの遷移先はHISTORYになっているので、履歴情報にある直近のステートに復帰します。
state=[{ ... },{ name=suspended transition=[{ guard=resume to=HISTORY }] }]
サブステートrunningからは以下の遷移設定によって、任意のステートでsuspendイベントが発生するとステートsuspendedに遷移します。
transition=[{ ... },{ guard=suspend to=suspended }]
実行
それでは実行してみます。
まず正常系とrejectイベントの2つのルートを確認します。これは前回と同じ動きになります。
この後に、今回のテーマであるサブステートを利用したsuspendイベント、resumeイベントの振る舞いを確認します。
正常系
最初に正常系の動きです。前回の正常系と同じ動きになります。
まず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に自動遷移しています。
suspendとresume
次はsuspendイベントです。サブステートrunning内の任意のステートでsuspendが可能になります。
状態機械purchaseを作った直後、状態機械はステートconfirmingになっています。
kaleidox> statemachine-new 'purchase StateMachine[purchase.running.confirming] kaleidox> setq sm StateMachine[purchase.running.confirming]
ステートconfirmingでsuspendイベントを出してみます。以下に示すとおりsuspended状態になりました。
kaleidox> event-issue 'suspend Event[cancel] kaleidox> sm StateMachine[purchase.suspended]
ここでresumeイベントを出すと、suspended前の状態であるステートconfirmingに戻りました。
kaleidox> event-issue 'resume Event[resume] kaleidox> sm StateMachine[purchase.running.confirming]
次には、ステートdeliveringでresume/suspendの動きを確かめます。
状態機械purchaseを作った後にconfirmイベントを出し、delivering状態にします。
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]
ステートdeliveringでsuspendイベントを出してみます。以下に示すとおりsuspended状態になりました。
kaleidox> event-issue 'suspend Event[suspend] kaleidox> sm StateMachine[purchase.suspended]
ここでresumeイベントを出すと、suspended前の状態であるステートdeliveringに戻りました。ステートsuspendに遷移する前のステートを覚えていて、そのステートに復帰することが確認できました。
kaleidox> event-issue 'resume Event[resume] kaleidox> sm StateMachine[purchase.running.delivering]
まとめ
今回はスブステートをもった状態機械モデルで保留機能をモデル定義し、Kaleidox上で動作させてみました。
前回実現したキャンセル機能と今回の保留機能は実用アプリケーションでは頻出の機能ですが、スクラッチで実装するのはなかなか大変だと思います。これらの機能がモデル定義のみで使用できることは、モデル駆動開発の大きなメリットといえます。
諸元
- Kaleidox
- 0.3.1