2021年6月30日水曜日

Kaleidox/状態機械 : サブステート

前回は状態機械のモデル定義を行い、そのままKaleidoxで動作させることができることを紹介しました。

前回の状態機械はステートがフラットに並んでいる単純なものでしたが、実際のアプリケーションを作成するにはやや不便なところがあります。

というのは、実際のアプリケーションではキャンセルや保留といった状態遷移が必須となってきますが、フラットなステートの遷移では表現が煩雑になったり、実現が困難になったりという問題があるからです。

この問題を解決する仕組みの一つがサブステートです。

今回はこのサブステートをつかってキャンセル機能を実現します。

モデル

前回のモデルに対して以下の拡張を行っています。

  • Cancelイベントを追加
  • 基幹部をサブステートとして、サブステート内の任意のステートでキャンセル可能にした

この拡張設定を行った状態機械の定義は以下になります。

  1. * event  
  2. event=[{  
  3.     name="confirm"  
  4.   },{  
  5.     name="reject"  
  6.   },{  
  7.     name="delivered"  
  8.   },{  
  9.     name="cancel"  
  10. }]  
  11. * statemachine  
  12. statemachine={  
  13.   name="purchase"  
  14.   state=[{  
  15.     name=init  
  16.     transition=[{  
  17.       to=running  
  18.     }]  
  19.   },{  
  20.     name=canceled  
  21.     transition=[{  
  22.       to=FINAL  
  23.     }]  
  24.   }]  
  25.   statemachine=[{  
  26.     name="running"  
  27.     state=[{  
  28.       name=applying  
  29.       transition=[{  
  30.         to=confirming  
  31.       }]  
  32.     },{  
  33.       name=confirming  
  34.       transition=[{  
  35.         guard=confirm  
  36.         to=confirmed  
  37.       },{  
  38.         guard=reject  
  39.         to=rejected  
  40.       }]  
  41.     },{  
  42.       name=confirmed  
  43.       transition=[{  
  44.         to=delivering  
  45.       }]  
  46.     },{  
  47.       name=rejected  
  48.       transition=[{  
  49.         to=FINAL  
  50.       }]  
  51.     },{  
  52.      name=delivering  
  53.       transition=[{  
  54.         guard=delivered  
  55.         to=delivered  
  56.       }]  
  57.     },{  
  58.       name=delivered  
  59.       transition=[{  
  60.         to=FINAL  
  61.       }]  
  62.     }]  
  63.     transition=[{  
  64.       guard=cancel  
  65.       to=canceled  
  66.     }]  
  67.   }]  
  68. }  

イベント

以下の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になっています。

  1. kaleidox> statemachine-new 'purchase  
  2. StateMachine[purchase.running.confirming]  
  3. kaleidox> setq sm  
  4. StateMachine[purchase.running.confirming]  

状態機械purchaseを作成すると、すぐにサブステートrunning内のステートconfirmingに遷移します。

次にevent-issue関数でconfirmイベントを発行します。

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。引き続きサブステートrunning内です。

  1. kaleidox> event-issue 'confirm  
  2. Event[confirm]  
  3. kaleidox> sm  
  4. StateMachine[purchase:delivering]  

次にevent-issue関数でconfirmイベントを発行します。

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。引き続きサブステートrunning内です。

  1. kaleidox> event-issue 'delivered  
  2. Event[delivered]  
  3. kaleidox> sm  
  4. StateMachine[purchase:FINAL]  

これで状態機械の状態遷移は終了です。

reject

続けてrejectイベントのルートも確認しておきます。

statemachine-new関数で状態機械purchaseを生成した直後はconfirming状態になっています。

ここでrejectイベントを発行するとモデルの定義どおり最終状態FINALに遷移しました。

  1. kaleidox> statemachine-new 'purchase  
  2. StateMachine[purchase.running.confirming]  
  3. kaleidox> setq sm  
  4. StateMachine[purchase.running.confirming]  
  5. kaleidox> event-issue 'reject  
  6. Event[reject]  
  7. kaleidox> sm  
  8. StateMachine[purchase:FINAL]  

状態機械purchaseはrejectイベントを受信することで状態rejectedに遷移しますが、状態rejectedから最終状態FINALの間にガードがないので最終状態FINALに自動遷移しています。

cancel

次はcancelイベントです。サブステートrunning内の任意のステートでcancelが可能になります。

まずステートconfirmingでcancelイベントを出してみます。以下に示すとおりFINAL状態になりました。状態機械がcancelイベントを受信した結果、ステートcanceledに遷移し、ステートcanceledにはガードを設定していないのでFINAL状態に遷移しています。

  1. kaleidox> statemachine-new 'purchase  
  2. StateMachine[purchase.running.confirming]  
  3. kaleidox> setq sm  
  4. StateMachine[purchase.running.confirming]  
  5. kaleidox> event-issue 'cancel  
  6. Event[cancel]  
  7. kaleidox> sm  
  8. StateMachine[purchase:FINAL]  

次にステートdeliveringでcancelイベントを出してみます。以下に示すとおりこちらもFINAL状態になりました。

  1. kaleidox> statemachine-new 'purchase  
  2. StateMachine[purchase.running.confirming]  
  3. kaleidox> setq sm  
  4. StateMachine[purchase.running.confirming]  
  5. kaleidox> event-issue 'confirm  
  6. Event[confirm]  
  7. kaleidox> sm  
  8. StateMachine[purchase.running.delivering]  
  9. kaleidox> event-issue 'cancel  
  10. Event[cancel]  
  11. kaleidox> sm  
  12. StateMachine[purchase:FINAL]  

いずれの場合もサブステートrunninigに設定した以下の遷移によって、サブステート内の状態からcanceled状態に遷移することができました。

  1. transition=[{  
  2.   guard=cancel  
  3.   to=canceled  
  4. }]  

まとめ

サブステートをもった状態機械モデルでキャンセル機能をモデル定義し、Kaleidox上で動作させてみました。

キャンセル機能は実際のアプリケーションでも頻出の機能ですが、実装するのは案外煩雑なので、サブステートを用いたシンプルなモデル定義のみでそのまま動作するのは大きなメリットだと思います。

サブステートを使った別の頻出機能として保留機能があります。次回は状態機械モデルとKaleidoxで保留機能を実現する予定です。

諸元

Kaleidox
0.3.0