DSL / The State Machine Model


ステートマシンモデル

要約

  • 次のステップはステートマシンをソフトウェアに組み込むことです.
  • ステートマシンのためのドメインモデルを作成します.

ステートマシンの構造

  • ソースコード参照
抽象イベント
コマンドとイベントの共通スーパークラス.4文字からなるコードと名前を持つ.
イベント
コントローラが受信する.
コマンド
コントローラが送信する.
状態
送信するコマンドと遷移先の状態を持つ.
ステートマシン
初期状態とリセットイベント(複数)を持つ.他の状態は初期状態から到達可能.
リセットイベント
ステートマシンを初期状態に戻すイベント.

ステートマシンの振る舞い

  • ソースコード参照
登録されていないイベント
無視する.
登録されているイベント
対象となる状態へと遷移し,対象となる状態に定義されたコマンドを実行する.

担当者のつぶやき


みんなの突っ込み

  • まとめの方にコメント入りのコードを書いたけど、原書の訳が少ないだけに、ここまで書くのはNGかな?(その際は消してください)-- 池田?&new{2009-08-16 (日) 03:13:47}

まとめ (議事録)

http://www.martinfowler.com/dslwip/intro/stateModel.gif

State

/* 状態を表すクラス。*/
class State...

   /* 状態の名前 */
   private String name;

   /* 送信されるコマンド郡 */
   private List<Command> actions = new ArrayList<Command>();

   /* 状態遷移を表すオブジェクトを格納した連想配列。キーは4桁のイベントコード */
   private Map<String, Transition> transitions = new HashMap<String, Transition>();

   /* 状態遷移を表すオブジェクトの追加 */
   public void addTransition(Event event, State targetState) {
       transitions.put(event.getCode(), new Transition(this, event, targetState));
   }

   /* 遷移先の状態のコレクション */
   Collection<State> getAllTargets() {
      List<State> result = new ArrayList<State>();

      //遷移先の状態が一意になるとは限らないと思うが、何故List?
      for (Transition t : transitions.values()) result.add(t.getTarget());

      return result;
  }

  /* 状態遷移が登録されているか? */
  public boolean hasTransition(String eventCode) {
     return transitions.containsKey(eventCode);
  }

  /* イベントコードに紐づいた状態を取得するメソッド */
  public State targetState(String eventCode) {
     return transitions.get(eventCode).getTarget();
  }

  /* コマンドの実行。チャネルによって処理内容を変える事が可能 */
  public void executeActions(CommandChannel commandsChannel) {
     for (Command c : actions) commandsChannel.send(c.getCode());
  }

StateMachine?

//起点となる状態から遷移可能な状態を管理するクラス
class StateMachine...

 //起点となる状態に戻ってくるイベントのリスト
 private List<Event> resetEvents = new ArrayList<Event>();

 public void addResetEvents(Event... events) {
   for (Event e : events) resetEvents.add(e);
 }

  //起点となる状態
  private State start;

  public StateMachine(State start) {
     this.start = start;
   }

 //起点となる状態から遷移可能な全ての状態を取得するメソッド
 public Collection<State> getStates() {
   List<State> result = new ArrayList<State>();
   gatherForwards(result, start);
   return result;
 }

 //起点となる状態から遷移可能な状態を再帰的にリストに格納するメソッド
 private void gatherForwards(Collection<State> result, State start) {
   if (start == null) return;
   if (result.contains(start)) return;
   else {
     result.add(start);
     for (State next : start.getAllTargets()) {
       gatherForwards(result, next);
     }
     return;
   }
 }

 //起点となる状態に戻ってくるイベントを登録するメソッド。
 private void addResetEvent_byAddingTransitions(Event e) {
   for (State s : getStates())
     if (!s.hasTransition(e.getCode())) s.addTransition(e, start);
 }

  public boolean isResetEvent(String eventCode) {
   return resetEventCodes().contains(eventCode);
 }

 private List<String> resetEventCodes() {
   List<String> result = new ArrayList<String>();
   for (Event e : resetEvents) result.add(e.getCode());
   return result;
 }

Controller

//状態を管理するコントローラー
//currentStateとmachineの整合性はどこで保障されている?
class Controller...

  //現在の状態
  private State currentState;

  //状態の遷移先を管理
  private StateMachine machine;

  public CommandChannel getCommandChannel() {
    return commandsChannel;
  }

  //状態に到達するチャネル(例:グラント嬢、スミス嬢、ショー嬢)
  protected CommandChannel commandsChannel;

  //イベントを実行するメソッド
  public void handle(String eventCode) {

    //現在の状態がイベントコードに対応する状態遷移を持っていれば実効
    if (currentState.hasTransition(eventCode))
      transitionTo(currentState.targetState(eventCode));

    //リセットを促すイベントコードである場合
    else if (machine.isResetEvent(eventCode))

       //起点となる状態に遷移する為の処理を実行
       transitionTo(machine.getStart());
       // ignore unknown events
    }

  //チャンネルを通して指定された状態に遷移する為の処理を実行
  private void transitionTo(State target) {
    currentState = target;
    currentState.executeActions(commandsChannel);
  }