2021年5月31日月曜日

Kaleidox/状態機械

オブジェクト指向分析設計(OOAD)によるソフトウェア開発を進めるための鍵となるのが状態機械です。

Javaなどのオブジェクト指向言語では状態機械は直接サポートはされておらず、状態機械を実現するためのクラスライブラリもよいものがありません。ワークフローエンジンといったものはありますが、仕掛けが大きすぎるので状態機械を実装するために手軽に使用できる感じではないでしょう。

このため、モデリング段階で状態機械を定義しても、プログラミング段階では手組みになってしまうので、モデリングすることの価値がそれほど高くないという状況になっています。また、状態機械をプログラミングで実装するのは案外大変です。

このこともあってか、開発現場では状態機械はほとんど使われていない状況だと思います。

OOADは静的構造モデル(クラス図)と動的モデル(状態機械)が両輪で、その2つのモデルと要求仕様をシナリオ分析であるユースケースモデルで連携させるという仕掛けになっています。

両輪の一つである動的モデルが使用されていないので、当然静的構造モデルと動的モデル、要求仕様を連携させるユースケースモデルも実力を発揮できない状況にあるといえます。

結果として、OOADではなくクラス図を用いたデータ中心設計になってしまっているのが現状ではないでしょうか。

クラウドアプリケーションでは、今後ますますイベント駆動型の応用が重要になってくると予想されますが、この核となるモデルが状態機械であり、状態機械がうまく活用されていない現状はかなり危機的な状況ではないかと思います。

SimpleModelingではこの問題に対処するため、モデルコンパイラSimpleModelerとアクション言語Kaleidoxで状態機械をサポートします。モデリング段階で状態機械モデルを定義することで、状態機械のプログラミングをせずに、アプリケーションでそのまま状態機械を利用できることになります。

今回はKaleidoxで状態機械を使用してみます。

モデル

Kaleidoxで状態機械を動かしてみましょう。

まず状態機械に使用するイベントを定義した後、状態機械のモデルを定義します。

商品販売を単純化したモデルにしてみました。本来は、注文毎に状態機械を持つことになりますが、ここでは簡単化のためグローバルな状態機械をグローバルなイベントで駆動することにします。

  1. * event  
  2. event=[{  
  3.     name="confirm"  
  4.   },{  
  5.     name="reject"  
  6.   },{  
  7.     name="delivered"  
  8. }]  
  9. * statemachine  
  10. statemachine={  
  11.   name="purchase"  
  12.   state=[{  
  13.     name=applying  
  14.     transition=[{  
  15.       to=confirming  
  16.     }]  
  17.   },{  
  18.     name=confirming  
  19.     transition=[{  
  20.       guard=confirm  
  21.       to=confirmed  
  22.     },{  
  23.       guard=reject  
  24.       to=rejected  
  25.     }]  
  26.   },{  
  27.     name=confirmed  
  28.     transition=[{  
  29.       to=delivering  
  30.     }]  
  31.   },{  
  32.     name=rejected  
  33.     transition=[{  
  34.       to=final  
  35.     }]  
  36.   },{  
  37.     name=delivering  
  38.     transition=[{  
  39.       guard=delivered  
  40.       to=delivered  
  41.     }]  
  42.   },{  
  43.     name=delivered  
  44.     transition=[{  
  45.       to=final  
  46.     }]  
  47.   }]  
  48. }  

イベント

以下の3つのイベントを定義しています。

confirm
確認OK
reject
確認却下
delivered
配送済み

状態機械

定義したモデルを状態機械図で記述すると以下になります。

実行

それでは実行してみます。

まずstatemachine-new関数で状態機械を生成します。生成する状態機械のモデル名はpurchaseです。

生成した状態機械をsetq関数で変数smに束縛します。

状態機械の名前はpurchaseです。状態機械の状態はconfirmingになっています。

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

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

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。

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

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

confirmイベント発行の結果、状態機械purchaseの状態はdeliveringになりました。

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

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

状態機械の遷移の履歴をhistoryメソッドで取得することができます。

  1. kaleidox> sm.history  
  2. ((Event[system.control.init] . State[applying]) (Event[system.control.go-ahead] . State[confirming]) (Event[confirm] . State[confirmed]) (Event[system.control.go-ahead] . State[delivering]) (Event[delivered] . State[delivered]) (Event[system.control.go-ahead] . State[final]))  

historyメソッドで取得した結果を元に状態遷移の経緯をまとめると以下になります。

イベント遷移先
system.control.initapplying
system.control.go-aheadconfirming
confirmconfirmed
system.control.go-aheaddelivering
delivereddelivered
system.control.go-aheadfinal

まず、初期状態から自動的に先頭の状態であるapplyingに遷移します。イベントは初期化を示す疑似イベントであるsystem.control.initです。

状態applyingと状態confirming間にはガードがないので状態間を自動的に遷移します。イベントは自動遷移を示す疑似イベントであるsystem.control.go-aheadです。状態機械モデルですが、アクティビティモデル的な振舞いになります。

ここでconfirming状態となります。

次にevent-issue関数でconfirmイベントを発行すると、confirming状態からconfirmed状態に遷移します。

状態confirmedと状態delivering間にはガードがないので状態間を自動的に遷移します。

ここでdelivering状態となります。

次にevent-issue関数でdeliveredイベントを発行すると、delivering状態からdelivered状態に遷移します。

状態deliveredと最終状態final間にはガードがないので状態間を自動的に遷移し、状態機械purchaseは終了します。

reject

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

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

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

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

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

まとめ

Kaleidoxで状態機械モデルの実行を行うことができるようになりました。

今回は最もシンプルなモデルですが、このレベルのモデルでもプログラミングレスで使用できるとかなり便利だと思います。

状態機械モデルはもっと複雑な記述が可能で、より広範囲な応用に適用することができます。次回以降、状態機械モデルの色々な使い方について見ていく予定です。

諸元

Kaleidox
0.2.1