なぜこの記事を書くのか
StateパターンはGoFのデザインパターンの中でも不遇だという記事を見た。あまり使えないパターンだということだろう。
確かに、私以外に使っている人を実務上で見たことがない。みんな知らないか、知っていてもifで書けばいいと思っているのだろう。
だが、私の知る限り、Stateパターンはかなり強力だ。理解には時間がかかったが、私の最も好きなデザインパターンの一つだと言える。
そんな世間とのかい離を感じつつ、Stateパターンが見直されることを願って、この記事を執筆する。
酔っぱらっているので後ほど加筆するかもしれない。
Stateパターンの説明
Stateパターンは、オブジェクトの状態に応じて、その振る舞いを変える仕組みだ。これだけだとStrategyパターンと同じだと思われるだろうが、基本的には、振る舞いを行った後に遷移が生じる。
Strategy + 状態遷移と考えると外れないだろう。
Stateパターンの洞察
現在のStateにおいて、呼び出されたメソッドに応じて振る舞いを行い、次のStateに遷移する。
それだけと言えばそれだけだが、ポイントは、状態遷移表を単純にコードに落とせるという点だろう。
State | Action1/transition | Action2/transition |
---|---|---|
State1 | print('foo')/State2 | print('foo2')/State1 |
State2 | print('baa')/State2 | print('baa2')/State3 |
State3 | print('hoge')/State1 | print('hoge2')/State1 |
上記のようなMealy型の状態遷移図を作れば、確実にコードに落とせる。
Stateごとにクラスが分かれていて、実装するメソッドも決まっているため、上記の表が書ければ機械的にコードを書くだけで良い。
コードレビューの際には、上記の表のチェックを入念に行う必要があるが、コード上のミスは簡単に見つけられるので、
表だけに集中すればよい。
Stateパターンの狙い
現代のソフトウェア開発の難しさは、設計と実装を等価に行うことの難しさにあると思う。設計上は完璧で、まったく無駄がなく、あらゆるユースケースに対して快適に動作する予定であっても、いざ実装を行おうとするとそれが実現できない場合や、実現する上で設計と異なってしまうことは往々にしてある。それをFeasibility Studyが足りないと断ずることは簡単だが(怒るだけならマネージャーは簡単だ)、ここ数十年の期間多くのプロジェクトが失敗している現実を見ると、やはりソフトウェア開発は非常に難しく、計画通りにいかないという点を認めるしかないのではないかと思う。
私は正直、500行くらいのコードなら頭の中で計画した通りに書けるようになったが、1000行を超えると想定外が生じて試行錯誤することが多い。つまり、実装途中に、あらかじめ想定していた設計ではうまく動かないことが分かり、ちょっと汚くなると分かりつつも変数やメソッドのscopeを広げて無理やり処理可能にすることがある。
Stateパターンでは、設計と実装のマッピングは非常にシンプルなので、大多数の労力は設計の正しさのチェックだけで済む。また、Stateの追加に伴う修正により、予期せぬ不具合が生じる可能性も非常に少ない。
表を正しく書けば、コードに不具合は発生しない。こんな当たり前のようなことが、現代のプログラミングにおいては非常に難しいが、これを実現できる数少ないパターンであることが、Stateパターンの魅力だと思う。
未来のソフトウェア開発は、きっと今のような困難は解決されている。大した役割を持っていない小さなクラスで、うっかり変数のnullチェックを忘れたからといって、NullPointerExceptionでシステム全体が落ちるなんてことはないはずだ。
Stateパターンは、そんな未来を先取りするかのような理想的なパターンで、設計が正しければ、実装も正しく行えるという夢のようなテクニックだ。