Namespace とステートパターン

今回は名前空間を使ってステートパターンにトライしてみたいと思います。基本となるのは、名前空間とステートを一対一に対応させようという考え方です。

以前 Sprite とマウスイベントという記事で Sprite でボタンが作れることに簡単に触れていますが、今回はそれをステートパターンで実装してみます。

では、ボタンとなるクラスの定義を始めましょう。とりあえず、ボタンに必要な状態の定義から始めます。

StatePatternButton.as:
public class StatePatternButton extends Sprite{
  // ボタンの状態を名前空間として3種類定義する
  private static namespace out;   // マウスがボタンの外にある
  private static namespace over;  // マウスがボタンの上にある
  private static namespace down;  // マウスがボタンの上で押された
 
  // それぞれの状態に応じた振舞いを定義
  out function handle(e:Event):Void {
    // out の状態での振舞いを記述
  }
 
  over function handle(e:Event):Void {
    // over の状態での振舞いを記述
  }
 
  down function handle(e:Event):Void {
    // down の状態での振舞いを記述
  }
}

今回は各ステートにおける振る舞いを一つのメソッドとして実装できると仮定しています。この部分はステート毎の差異しだいで変数の宣言になったり共通のインターフェースを継承したクラスになったりすることと思います。

次にステート変更用の手段を定義します。

  // 現在のステートを保持する変数
  private var currentState:Namespace;
 
  // 新しいステートを設定する関数
  private function changeState(newState:Namespace):Void {
    currentState= newState;
  }

現在のステートを currentState という変数に保持することにします。ステートの変更には changeState 関数を使用します。

さらに、マウスイベントの種類と名前空間(ステート)の関連を定義します。

  // 連想配列の作成
  private var stateMap:Object = new Object();
 
  // イベントの種類とステートの関連付け
  private function initStateMap():Void {
  	stateMap[MouseEventType.MOUSE_OUT] = out;
  	stateMap[MouseEventType.MOUSE_OVER] = over;
  	stateMap[MouseEventType.MOUSE_UP] = over;
  	stateMap[MouseEventType.MOUSE_DOWN] = down;
  }
 
  // イベントタイプに応じたステートを返す
  private function getState(e:Event):Namespace {
    // ほんとは引数や戻り値が null だった場合の考慮も必要
    return stateMap[e.type] as Namespace;
  }

というわけでマウスイベントハンドラの定義です。呼び出される処理はイベントの種類ではなくステートに応じて呼び出されるため、イベントハンドラは一つだけです。イベントハンドラからは handleEvent という関数を呼び出します。この関数の内部でステートに応じた処理を呼び出します。

  // イベントハンドラ(種類に関係なく使用される)
  private function onMouseEvent(e:Event):Void {
    var ns:Namespace = getState(e);
    changeState(ns);
    handleEvent(e);
  }
 
  // 現ステート用の処理を呼び出す関数
  private function handleEvent(e:Event):Void {
    currentState::handle(e);
  }

あとは、コンストラクタ内で必要な初期化を行えばおしまいです。

  // 初期状態のステートを定義
  private static var initState:Namespace = out;
 
  public function StatePatternButton() {
    // ボタンとして動作するように設定
    buttonMode = true;
 
    // イベント種類とステートの対応表作成
    initStateMap();
 
    // 関連するマウスイベントにリスナ関数を登録
    addEventListener(MouseEventType.MOUSE_OUT, onMouseEvent);
    addEventListener(MouseEventType.MOUSE_OVER, onMouseEvent);
    addEventListener(MouseEventType.MOUSE_UP, onMouseEvent);
    addEventListener(MouseEventType.MOUSE_DOWN, onMouseEvent);
 
    // ステートの初期化
    changeState(initState);
    // 他に必要な初期化があれば
    // initSomethingElse();
  }
}

イベントの種類とハンドラ名を関連付ける代わりに、イベントタイプと名前空間(=ステート)を関連付けてみました。MOUSE_UP と MOUSE_OVER に同じ関数名を関連付けるよりはちょっと気持ちいい感じです。

以下のようなクラスを作って実際に動かしてみましょう。

package {
  import flash.display.Sprite;
 
  public class StatePattenTest extends Sprite {
    public function StateTest() {
      addChild(new StateButton());
    }
  }
}

というわけで名前空間の使用例を紹介してみました。名前空間はなじみがないとなんの役に立つのか想像しにくいものです。とりあえずでも名前空間の使い方について考えるきっかけになればと思います。

コメントする

2014年1月

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
レンタルサーバー

月別 アーカイブ

Powered by Movable Type 4.261