State パターン

State パターンとは、プログラミングで用いられる振る舞いに関するパターン デザインパターンの一種である。このパターンはオブジェクトの状態を表現するために用いられる。ランタイムでそのタイプを部分的に変化させるオブジェクトを扱うクリーンな手段となる395}}。

1. 擬似コードによる例
------------

ドローソフトを例に取る。このプログラムは任意の時点においてさまざまなツールのうちの1つとして振る舞うマウスカーソルを持つ。複数のカーソルオブジェクトを切り替える代わりに、カーソルは現在使用されているツールを表す内部的な状態を保持する。ツールに依存するメソッドが呼ばれると、メソッド呼び出しはカーソルの状態へと渡される。

各ツールは1つの状態に対応している。共有される抽象状態クラスはAbstractToolである。

class AbstractTool isfunction moveTo isinput:  the location //point// the mouse moved to////

function mouseDown isinput:  the location //point// the mouse is at////

function mouseUp isinput:  the location //point// the mouse is at////

この定義により、各ツールはマウスカーソルの移動およびクリック／ドラッグの開始と終了を扱わなければならない。

その基底クラスを用い、単純なペンと範囲選択の各ツールを定義すると以下のようになる。

subclass PenTool of AbstractTool is last_mouse_position := invalid mouse_button := up

function moveTo isinput:  the location //point// the mouse moved toif mouse_button = down//// last_mouse_position := point

function mouseDown isinput:  the location //point// the mouse is at mouse_button := down last_mouse_position := point

function mouseUp isinput:  the location //point// the mouse is at mouse_button := up

subclass SelectionTool of AbstractTool is selection_start := invalid mouse_button := up

function moveTo isinput:  the location //point// the mouse moved toif mouse_button = down////

function mouseDown isinput:  the location //point// the mouse is at mouse_button := down selection_start := point

function mouseUp isinput:  the location //point// the mouse is at mouse_button := up

この例では、コンテキストのためのクラスはCursorと呼ばれている。抽象状態クラスで名付けられたメソッド群はコンテキストにおいても実装されている。コンテキストクラスではこれらのメソッドは、current_toolにより表される現在の状態の、対応するメソッドを呼び出す。

class Cursor is current_tool := new PenTool

function moveTo isinput:  the location //point// the mouse moved to current_tool.moveTo

function mouseDown isinput:  the location //point// the mouse is at current_tool.mouseDown

function mouseUp isinput:  the location //point// the mouse is at current_tool.mouseUp

function usePenTool is current_tool := new PenTool

function useSelectionTool is current_tool := new SelectionTool

Cursorオブジェクトが、適切なメソッド呼び出しをアクティブとなっているツールへと渡すことにより、異なる時点においてPenToolとSelectionToolのどちらとしてでも振舞えるのである。これがStateパターンの本質である。この場合では、PenCursorクラスとSelectCursorクラスを作ることで状態とオブジェクトを結合することも可能であり、単なる継承へと単純化できたであろうが、実践においては新しいツールが選択される毎に新しいオブジェクトへとコピーするにはコストがかかりすぎたりエレガントでなかったりするようなデータをCursorが保持していることもあるであろう。

2. Javaによる例
-----------

以下は状態のインタフェースと2つの実装である。状態メソッドはコンテキストオブジェクトへの参照を持ち、その状態を変えることができる。

interface State { void writeName; }

class StateA implements State { public void writeName { System.out.println); // コンテキストをStateBに遷移する stateContext.setState); } }

class StateB implements State { private int count = 0; public void writeName{ System.out.println); // StateBのwriteNameが2度呼び出された後にコンテキストをStateAに遷移する if  { stateContext.setState); } } }<WtXmlEndTag />

コンテキストクラスは、初期状態でインスタンス生成したところの状態変数を持つ。そのメソッドでは、状態オブジェクトの対応するメソッドを使用する。

public class StateContext { private State myState; public StateContext { setState); }

// 通常は、Stateインタフェースを実装しているクラスによってのみ呼び出される public void setState { this.myState = newState; }

public void writeName { this.myState.writeName; } }<WtXmlEndTag />

用法は以下。 public class TestClientState { public static void main { StateContext sc = new StateContext; sc.writeName; sc.writeName; sc.writeName; sc.writeName; sc.writeName; sc.writeName; } }

TestClientStateからのmainの出力は次のようになるはずである。 monday TUESDAY WEDNESDAY thursday SATURDAY SUNDAY

3. 脚注
-----

4. 関連項目
-------

有限オートマトン
Strategy パターン

5. 外部リンク
--------

[1]