振る舞い(behavior)に関するパターンの1つ、Stateについて。
[0回]
状態によって振る舞いが変化するものって結構たくさんあると思うんです。
振る舞いを変化させるためのわかりやすい対応策は、
Switch文やif文による条件分岐処理でしょう。
しかしながら、扱う状態数が多くなってくるとこの方法はやや手間がかかります。
オブジェクトの内部状態によって振る舞いが変わるすべての処理において、毎回同じ構造のSwitch(あるいはif)が出現します。新しい状態がが導入されたりすれば、ソフトウェアはまとめてリコンパイルするはめになるでしょう。
状態依存な部分と非依存の部分が分離されていないがために、状態数の追加に対して"修正"で対処しなくてはならなくなります。
また、条件分岐のコードが散在することになるので、プログラマが記述漏れをしてしまう・・・という危険もあります。コンパイルエラーを起こさない類のエラーですので、コード量に比例してデバッグ作業も面倒なものになるでしょう。
「状態依存」な部分をうまく分離できたとすれば、この問題は避けることができます。
コード中に分岐文なんか書かなくても済みます。
この分離のためのアプローチがStateパターンです。
[クラス図]
※このクラス図は"TECHSCORE"より引用しました。画像をクリックすると別窓でジャンプします。
登場人物:
・(abstract) Context クライアント側へのインタフェースを定義している
状態を表すクラス、State(の具象サブクラス)のインスタンスを持っている。
・(abstract) State Contextから抽出された"状態"を表すクラス。
・ConcleteState 具体的に「取りうる状態」がサブクラス化される。
(例)プロセスの状態であればrun,destory,resume,suspend,とか。
※この図には記載がありませんが、Contextのインタフェースを実際に利用することになるクラス、「Client」が存在しています。
おそらく当たり前すぎるから省略されているのでしょうが、Clientクラスと、パターンに登場するクラスとの関係は私的に超大事です。C言語に馴染んでると読み進むうちにここらへんの関係が迷子になります。
すべての状態はStateのサブクラスとして定義され、状態依存の処理はStateのメソッドとなります。
こうすることで、状態に依存するコードをStateの中に局所化できます。
新しい状態が定義されれば、プログラマはサブクラス化によってこれを定義することができ、付リグラマがSwitch文をくまなくスキャンする手間は省かれます。保守や拡張が容易になります。
分岐文による長ったらしいコードは見た目にも美しくないし、処理の本質が理解しづらくなります。私自身が実感してないので自分の言葉で書くことはできませんが、Stateパターンによってそうした効果を得ることができます。
ここからは私なりの総括です。
状態遷移を伴うシステムを表現したオブジェクトがContext。
システムなので当然利用者(上位システムとか、連携して動作している別のシステム(オブジェクト)だったり)が存在していて、Contextはそのためのインタフェースを提供している。
この中には当然状態に関する情報やそれに依存する振る舞いも含まれていて、Contextがなにかしら内部でうまくやりくりして機能を実現することになる。
しかし、Contextだけですべてをやろうとせず、一部の責任(状態うんぬんの部分)を内部で別のオブジェクト=Stateに任せてしてしまうことにする。
しかしながら・・・結局のところ、
クライアントからしてみれば、必要なのはContextが提供するインタフェースだけです。内部に何があって、何が起きてるかー、なんてことはクライアント側からすればどうでもいいことなので、
StateとかConcleteStateがどうこう、っていうのは所詮ブラックボックスの中の話です。クライアントには関係ありません。
ただ、
Stateを適用したら、中のコードがすっきりするよね!
中の人にとっても色々優しいよね!っていうのがStateのウリなんだと思います。
感想としましては、
C言語脳だと、Contextの立ち位置がよくわからくて理解しづらいのかもしれないです。少なくとも私がすんなり理解できなかったのはそういう部分だと思っています。
「クライアント」と、パターン中の「登場人物」の立ち位置を想像してみることが、オブジェクト指向に一歩近づけるコツのような気がいたします。
***
参考書籍。
***
[投稿・修正Log]
2012/6/16 Fri.
Ver.0.1 投稿
2012/6/20 Wed.
クラス図とTECHSCOREへのリンクを追加
まとめ追加
PR