SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

状態遷移表からStateパターンを自動生成する

T4 templateで楽にコードを書こう


  • X ポスト
  • このエントリーをはてなブックマークに追加

状態遷移表からコード生成

 それでは本番、状態(S)、事象(E)そして状態遷移表からStateパターンに基づいたC#コードを生成します。状態遷移表は「状態、事象、遷移先」の新たな状態の3つ組(例えば"disable状態でknockされたらenable状態に遷移"であれば { S.disabled, E.knock, S.enabled })をその組み合わせの数だけ列挙することとします。前回のアーティクルで例示した"ノック式ボールペン"であれば、以下のようなテンプレートになるでしょう。

State.tt
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>

<#@ include file="StatePattern.cstt" #>

<#+

    // NONEは状態を遷移しない(現状態を維持する)ことを示す特殊な遷移先
    enum S { disabled, enabled, NONE }; // 状態を列挙
    enum E { knock, write };  // 事象を列挙

    // 以下に書かれた状態遷移表に基づいてコードを生成する
    Tuple<S,E,S>[] _transition_ = {
      //           状態,       事象,    次の状態
      Tuple.Create(S.disabled, E.knock, S.enabled),
      Tuple.Create(S.disabled, E.write, S.NONE),

      Tuple.Create(S.enabled,  E.knock, S.disabled),
      Tuple.Create(S.enabled,  E.write, S.NONE),
    };

#>

 前述のBody.tt.includeと同じ要領で、このテンプレートからC#コードに変換されるStatePattern.csttを用意すればいい。僕は最終的に得られる(生成させたい)C#コードを手書きし、動作を確認したのちプリントアウトしました。そして状態や事象など、差し替えの必要な部分/繰り返しで表現できる部分にマーカーで色を付け、それを基に<#~#><#=~#><#+~#>を埋め込んでいきました。

StatePattern.cstt
  public abstract partial class State {
<#  foreach ( string s in Enum.GetNames(typeof(S)) ) {
      if ( s.ToUpper() == "NONE" ) { #>
    public static State <#= s #>() { return null; }
<#    } else { #>
    private static State <#= s #>_ = new <#= s #>();
    public static State <#= s #>() { return <#= s #>_; }
<#    }
    } #>

    partial void _trace_(string st, string ev);
    public abstract string name();
    protected abstract void do_enter(Context ctx);
    protected abstract void do_exit(Context ctx);
    public void start(Context ct) { do_enter(ct); }
    public void finish(Context ct) { do_exit(ct); }

<# foreach ( string e in Enum.GetNames(typeof(E)) ) { #>
    protected virtual void do_action(<#= e #> ev, Context ct, ref State ns) {}
    protected virtual State transition(<#= e #> ev)
      { throw new System.NotImplementedException(name()+".<#= e #>"); }
    public State handle(<#= e #> ev, Context ct) { 
      _trace_(name(),"<#= e #>");
      State ns = transition(ev);
      do_action(ev, ct, ref ns);
      if ( ns == null ) { ns = this; } else { do_exit(ct); ns.do_enter(ct); }
      return ns;
    }
<#  } #>
  }

<#  foreach ( var s in Enum.GetNames(typeof(S)) ) { 
      if ( s.ToUpper() == "NONE" ) continue; #>
  partial class <#= s #> : State {

    public override string name() { return "<#= s #>"; }
    partial void _enter_(Context ct);
    partial void _exit_(Context ct);
    protected override void do_enter(Context ctx) { _enter_(ctx); }
    protected override void do_exit(Context ctx) { _exit_(ctx); }

<#    foreach ( var t in _transition_ ) {
        if ( t.Item1.ToString() == s ) { #>
    protected override State transition(<#= t.Item2 #> ev) { return <#= (t.Item3.ToString().ToUpper() == "NONE") ? "null" : "State."+t.Item3.ToString()+"()" #>; }
    partial void _action_(<#= t.Item2 #> ev, Context ct, ref State ns);
    protected override void do_action(<#= t.Item2 #> ev, Context ct, ref State ns) { _action_(ev,ct,ref ns); } 
<#      }
      } #>
  }

<#  } #>

次のページ
Penの完成

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5783 2011/05/27 19:12

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング