【デザインパターン】State

どんなもの?

  • 状態が変わったときに、オブジェクトの振る舞いが変わるようにする。
  • それぞれの状態をクラスで表現する。
  • 状態を持つクラスは、それぞれの状態クラスを抽象クラスの形で保持する。
  • オブジェクト指向ステートマシン
  • Wrapper + polymorphic wrappee + collaboration

どんなときに使う?

  • 条件(状態)に一致するか否かを判定してそれぞれ処理したいとき。
    特に以下の場合に有効
    • 条件分岐数が多いとき
    • 同じ条件分岐が複数の箇所に点在するとき

典型的な構成要素

  • State
    • 状態を表すクラス
    • 状態ごとに振る舞いが異なる関数のインターフェースを定義する
  • ConcreteStateA
  • ConcreteStateB
    • Stateのインターフェースを実装する
    • 具体的な状態を1クラス=1状態で定義する
    • 1つの状態を表すのに複数のオブジェクトは必要ないため、Singletonを適用する
  • Context(状況判断)
    • 現在の状態を保持する
    • 利用者へのインターフェースを定義する
    • 状態を変更する関数を定義する
  • Client

    コード

#include <iostream>
#include <string>

using namespace std;

class Context;
//class ConcreteStateB;

class State{
public:
  virtual void stateMethod1(Context* context, int condition) = 0;
  virtual void stateMethod2(Context* context) = 0;
};

class Context{
public:
  Context(State* state){
    this->state = state;
  }
  void setState(State *state){
    this->state = state;
  }
  void contextMethod1(int condition){
    state->stateMethod1(this, condition);
  }
  void contextMethod2(){
    state->stateMethod2(this);
  }
  void contextMethod3(string msg){
    cout << msg << endl;
  }
private:
  State *state;

};

class ConcreteStateA: public State{
public:
  static State* getInstance(){
    static ConcreteStateA concretestateA;
    return &concretestateA;
  }
private:
  ConcreteStateA(){}
  void stateMethod1(Context* context, int condition){
    if(condition == 1){
      //      context->setState(ConcreteStateB::getInstance());
    }
  }
  void stateMethod2(Context* context){
    context->contextMethod3("test() at ConcreteStateA");
  }
};

class ConcreteStateB: public State{
public:
  static State* getInstance(){
    static ConcreteStateB concretestateB;
    return &concretestateB;
  }
private:
  ConcreteStateB(){}
  void stateMethod1(Context* context, int condition){
    if(condition == 0){
      context->setState(ConcreteStateA::getInstance());
    }
  }
  void stateMethod2(Context* context){
    context->contextMethod3("test() at ConcreteStateB");
  }
};
  


int main(){
  Context* context = new Context(ConcreteStateA::getInstance());
  for(int i=0;i<5;i++){
    context->contextMethod1(i%2);
    context->contextMethod2();
  }
}