旧来よりフラグを用いることでその時の状態に応じたふるまいを実現することが多い.
その一方で,フラグの管理が煩雑になりがちであり,ロジック上のバグの原因となりうる.
フラグとしてはbool型やenum型が用いられる.
そのような煩雑なフラグ管理を改善するパターンがState Patternである.
State Patternでは,状態,イベント,状態遷移の三種を定義する.
ある状態の時にあるイベントが発生したら,あるアルゴリズムを実行し,状態を変更するというものである.
これをオブジェクト指向的に実装したのがState Patternである.
非テンプレートによる簡単な実装ではStateの基本クラスをもたせ,仮想関数を呼び出せば良い.
しかし,イベントが5種に対してフラグが10種だとしたらどうだろうか.
単純に10種類のフラグ管理とイベント5種に対する振り分けが必要となる.
それをひとつのswitch文の中に記述してしまうと非常に煩雑なことになる.
上記のように状態自体をクラス化してしまい,その状態の時のイベントを状態クラスの中に記述することで非常に見通しのよいコードとなる.
これは特別な構造ではなく,現在の状態を示すクラスのポインタを基本クラスのポインタとして保有する場合は全てState Patternと言える.例えば,GUIの表示モードが複数ある場合,現在のView Classのポインタを持たせていれば一種のState Patternである.
その一方で,フラグの管理が煩雑になりがちであり,ロジック上のバグの原因となりうる.
フラグとしてはbool型やenum型が用いられる.
そのような煩雑なフラグ管理を改善するパターンがState Patternである.
State Patternでは,状態,イベント,状態遷移の三種を定義する.
ある状態の時にあるイベントが発生したら,あるアルゴリズムを実行し,状態を変更するというものである.
これをオブジェクト指向的に実装したのがState Patternである.
非テンプレートによる簡単な実装ではStateの基本クラスをもたせ,仮想関数を呼び出せば良い.
class ContextSwitch { public: static State* turn_on; // = new StateSwitchOn; static State* turn_off;// = new StateSwitchOff; void RequestPush() { mpCurrentState = mpCurrentState->RequestPush(); } private: State* mpCurrentState; }; class StateSwitch { public: State* RequestPush() { return DoPush(); } protected: virtual State* DoPush() = 0; }; class StateSwitchOn : public StateGate { protected: virtual State* DoPush() { return ContextGate::turn_off; } }; class StateSwitchOff : public StateGate { protected: virtual State* DoPush() { return ContextGate::turn_on; } };この場合はイベントが1種で状態が2種であるからコードが複雑になったかのように見える.
しかし,イベントが5種に対してフラグが10種だとしたらどうだろうか.
単純に10種類のフラグ管理とイベント5種に対する振り分けが必要となる.
それをひとつのswitch文の中に記述してしまうと非常に煩雑なことになる.
上記のように状態自体をクラス化してしまい,その状態の時のイベントを状態クラスの中に記述することで非常に見通しのよいコードとなる.
これは特別な構造ではなく,現在の状態を示すクラスのポインタを基本クラスのポインタとして保有する場合は全てState Patternと言える.例えば,GUIの表示モードが複数ある場合,現在のView Classのポインタを持たせていれば一種のState Patternである.
より複雑なことをさせるためには,Doの中で特定の関数を呼び出したい時がある.
例えば,上記例ではスイッチをONにしたときにはLightOnをOFFにしたときにはLightOffを呼びたい.
このLightOn,LightOffは当然Lightクラスがもつことになる.
このような場合は,メンバ関数ポインタをStateクラスに登録する.
例えば,上記例ではスイッチをONにしたときにはLightOnをOFFにしたときにはLightOffを呼びたい.
このLightOn,LightOffは当然Lightクラスがもつことになる.
このような場合は,メンバ関数ポインタをStateクラスに登録する.
StatePatternの構造をライブラリ化したものがBoostに二種類ある.Boost.StateChartとBoost.MSMである.今回はBoost.MSMをとりあげる.
これは,状態遷移マップをテンプレートで記述できる.上記例は下記のようになる.
これは,状態遷移マップをテンプレートで記述できる.上記例は下記のようになる.
struct Push {}; struct Light_ : public msm::front::state_machine_def<Light_> { struct SwitchOff{}; struct SwitchOn{}; typedef SwitchOff initial_state; void LightOn(const Push& ) { std::cout << "*" << std::endl; } void LightOff(const Push& ) { std::cout << "o" << std::endl; } struct trasition_table : mpl::vector< a_row < SwitchOff, Push, StateSwitchOn, &Light::LightOn>, a_row < SwitchOn, Push, StateSwitchOff, &Light::LightOff> > {}; } typedef msm::back::state_machine<Light_> Light; int main() { Light light; light.start(); light.progress_event(Push()); light.progress_event(Push()); return 0; }
コメントをかく