State パターン
State パターン(英: state pattern、ステート・パターン)とは、プログラミングで用いられる振る舞いに関する デザインパターンの一種である。このパターンはオブジェクトの状態(state)を表現するために用いられる。ランタイムでそのタイプを部分的に変化させるオブジェクトを扱うクリーンな手段となる[1]:395。
![](http://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/State_Design_Pattern_UML_Class_Diagram.svg/300px-State_Design_Pattern_UML_Class_Diagram.svg.png)
![](http://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/State_pattern_in_LePUS3.svg/300px-State_pattern_in_LePUS3.svg.png)
擬似コードによる例
編集ドローソフトを例に取る。このプログラムは任意の時点においてさまざまなツールのうちの1つとして振る舞うマウスカーソルを持つ。複数のカーソルオブジェクトを切り替える代わりに、カーソルは現在使用されているツールを表す内部的な状態を保持する。(例えばマウスクリックの結果として)ツールに依存するメソッドが呼ばれると、メソッド呼び出しはカーソルの状態へと渡される。
各ツールは1つの状態に対応している。共有される抽象状態クラスはAbstractToolである。
class AbstractTool is function moveTo(point) is input: the location point the mouse moved to (this function must be implemented by subclasses) function mouseDown(point) is input: the location point the mouse is at (this function must be implemented by subclasses) function mouseUp(point) is input: the location point the mouse is at (this function must be implemented by subclasses)
この定義により、各ツールはマウスカーソルの移動およびクリック/ドラッグの開始と終了を扱わなければならない。
その基底クラスを用い、単純なペンと範囲選択の各ツールを定義すると以下のようになる。
subclass PenTool of AbstractTool is last_mouse_position := invalid mouse_button := up function moveTo(point) is input: the location point the mouse moved to if mouse_button = down (draw a line from the last_mouse_position to point) last_mouse_position := point function mouseDown(point) is input: the location point the mouse is at mouse_button := down last_mouse_position := point function mouseUp(point) is input: the location point the mouse is at mouse_button := up subclass SelectionTool of AbstractTool is selection_start := invalid mouse_button := up function moveTo(point) is input: the location point the mouse moved to if mouse_button = down (select the rectangle between selection_start and point) function mouseDown(point) is input: the location point the mouse is at mouse_button := down selection_start := point function mouseUp(point) is input: the location point the mouse is at mouse_button := up
この例では、コンテキストのためのクラスはCursorと呼ばれている。抽象状態クラス(この場合AbstractTool)で名付けられたメソッド群はコンテキストにおいても実装されている。コンテキストクラスではこれらのメソッドは、current_toolにより表される現在の状態の、対応するメソッドを呼び出す。
class Cursor is current_tool := new PenTool function moveTo(point) is input: the location point the mouse moved to current_tool.moveTo(point) function mouseDown(point) is input: the location point the mouse is at current_tool.mouseDown(point) function mouseUp(point) is input: the location point the mouse is at current_tool.mouseUp(point) function usePenTool() is current_tool := new PenTool function useSelectionTool() is current_tool := new SelectionTool
Cursorオブジェクトが、適切なメソッド呼び出しをアクティブとなっているツールへと渡すことにより、異なる時点においてPenToolとSelectionToolのどちらとしてでも振舞えるのである。これがStateパターンの本質である。この場合では、PenCursorクラスとSelectCursorクラスを作ることで状態とオブジェクトを結合することも可能であり、単なる継承へと単純化できたであろうが、実践においては新しいツールが選択される毎に新しいオブジェクトへとコピーするにはコストがかかりすぎたりエレガントでなかったりするようなデータをCursorが保持していることもあるであろう。
Javaによる例
編集以下は状態(State)のインタフェースと2つの実装である。状態メソッドはコンテキストオブジェクトへの参照を持ち、その状態を変えることができる。
interface State {
void writeName(StateContext stateContext, String name);
}
class StateA implements State {
public void writeName(StateContext stateContext, String name) {
System.out.println(name.toLowerCase());
// コンテキストをStateBに遷移する
stateContext.setState(new StateB());
}
}
class StateB implements State {
private int count = 0;
public void writeName(StateContext stateContext, String name){
System.out.println(name.toUpperCase());
// StateBのwriteName()が2度呼び出された後にコンテキストをStateAに遷移する
if (++count > 1) {
stateContext.setState(new StateA());
}
}
}
コンテキストクラスは、初期状態(この場合はStateA)でインスタンス生成したところの状態変数を持つ。そのメソッドでは、状態オブジェクトの対応するメソッドを使用する。
public class StateContext {
private State myState;
public StateContext() {
setState(new StateA());
}
// 通常は、Stateインタフェースを実装しているクラスによってのみ呼び出される
public void setState(State newState) {
this.myState = newState;
}
public void writeName(String name) {
this.myState.writeName(this, name);
}
}
用法は以下。
public class TestClientState {
public static void main(String[] args) {
StateContext sc = new StateContext();
sc.writeName("Monday");
sc.writeName("Tuesday");
sc.writeName("Wednesday");
sc.writeName("Thursday");
sc.writeName("Saturday");
sc.writeName("Sunday");
}
}
TestClientStateからのmain()の出力は次のようになるはずである。
monday
TUESDAY
WEDNESDAY
thursday
SATURDAY
SUNDAY
脚注
編集- ^ a b Erich Gamma; Richard Helm, Ralph Johnson, John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0201633612 (エリック・ガンマ、ラルフ・ジョンソン、リチャード・ヘルム、ジョン・ブリシディース(著)、グラディ・ブーチ(まえがき)、本位田真一、吉田和樹(監訳)、『オブジェクト指向における再利用のためのデザインパターン』、ソフトバンクパブリッシング、1995年。ISBN 978-4797311129)
- ^ a b State pattern in UML and in LePUS3
- ^ legend