今回はStateパターン編です。
Stateパターンはオブジェクト指向のポリモフィズムで動きを切り替えるという点で前回の
Strategyパターンと同じです。
ただし、Stateパターンはそのインスタンス自身が「状態」を表現するという点で異なります。
電卓のUIのStateを作る
まず、Stateの使いどころですが、ここでは以前にわんくま同盟で取り上げられた
電卓を例にしてみましょう。
επιστημηさんのところに
電卓を状態遷移で捉える
話題がでていたのですが、この状態遷移というのがStateパターンの使いどころになります。
電卓基本動作の状態遷移表
|
初期状態 |
数値入力状態 |
数式作成状態 |
計算結果表示状態 |
数字入力 |
なにかする →数値入力状態 |
なにかする →数値入力状態 |
なにかする →数値入力状態 |
なにかする →数値入力状態 |
演算子入力 |
なにかする →初期状態 |
なにかする →数式作成状態 |
なにかする →数式作成状態 |
なにかする →数式作成状態 |
'='入力 |
なにかする →初期状態 |
なにかする →計算結果表示状態 |
なにかする →計算結果表示状態 |
なにかする →計算結果表示状態 |
'C'入力 |
なにかする →初期状態 |
なにかする →初期状態 |
なにかする →初期状態 |
なにかする →初期状態 |
この、「初期状態」「数値入力状態」「数式作成状態」「計算結果表示状態」がStateパターンでは
オブジェクトのインスタンスで表現されます。
そして、そのクラスには「数字入力」「演算子入力」「'='入力」「'C'入力」の4つのメソッドが定義されます。
Java
/** @param <R> Stateの具象型 */
public interface State<R extends State> {
/** 数値の入力
* @return 遷移するState. 以下同じ */
R onInputNumber(int num);
/** 演算子の入力 */
R onInputOperation(Operation ope);
/** '='の入力 */
R onInputEquale();
/** 'C'の入力 */
R onInputClear();
}
C#
public interface State<R> where R : State {
/// <summary>数値の入力</summary>
/// <returns>遷移するState. 以下同じ</returns>
R onInputNumber(int num);
/// <summary>演算子の入力</summary>
R onInputOperation(Operation ope);
/// <summary>'='の入力</summary>
R onInputEquale();
/// <summary>'C'の入力</summary>
R onInputClear();
}
より抽象的にすることで再利用性を持たせる
通常のStateパターンだと、この時点で具象型を用いるのでジェネリクスの出番はないところですが、
電卓と同じユーザインターフェースを持つ別のアプリケーションを作れるようにジェネリクスで
抽象化しておきます。
ご覧の通り、各メソッドは処理をした上で次に遷移するべきStateを返すようになっています。
このときに自信の型を返したいので自己言及するジェネリクス(
Java版、
C#版)
で取り上げた手法を用います。
これらの実装を作る際にはジェネリクス(サンプルソースのR)を具象化するわけですが、この型は
電卓用であれば電卓用、同一UIを用いた別の何かであればそれ用に独自に拡張します。
電卓用のStateと別の何かのStateは混同して利用されることはありません。
つまり、このUIを共通に提供するような再利用性の高いフレームワークを設計する場合でないと、
このようなStateパターンの多様な拡張性を持たせると言う設計は活きてこないのです。
逆に、このようなジェネリクスを活用することで、今まで再利用が不可能だと思っていたパーツを
再利用可能な形でライブラリとすることができます。
補足
Javaの場合はさらに
Strategyのインスタンスをenumで扱う
で取り上げたように、Stateのインスタンスをenum化することができます。
投稿日時 : 2008年6月5日 21:17