ようこそ。睡眠不足なプログラマのチラ裏です。

デザインパターン第4回「Strategyパターン」


今回はGoFデザインパターンの中から
「Strategyパターン」をぬるーく解説します。


もしこのパターンを知らないならば、このパターンを学ぶことを通して、
「インターフェイスの便利さ」を味わってもらえたらなあ、とか思う。


■Strategyパターンの概要■
Strategyとは英語で「戦略」を意味する言葉です。
このパターンは、将棋やオセロゲームなどのCPUの思考ルーチンを切り替えるときなどに使われています。
それが「戦略」という名の由来かどうかはわかりませんが、まあそんな感じです。
(「特定の条件において何をやりたいのか」を「戦略」と考えるとよい。)


このパターンは、戦略に応じた複数のアルゴリズムを用意し、カプセル化します。
利用する側は用意されたアルゴリズムを戦略に応じて使い分けるようにします。
これはすなわち、同じ振る舞いに対して異なるアルゴリズムを提供できることを意味します。
このように、アルゴリズムを抽出して汎化するパターンのことを、Strategyパターンと呼びます。


設計について深く意識せずにプログラミングをすると、
メソッドにアルゴリズムを含める形で実装してしまうことがよくあります。
メソッド内のif文やswitch文などの条件分岐に対して、直接アルゴリズムを埋め込むような処理を行ってしまうと、
追加や変更が発生したとき、他のアルゴリズムへ影響が及んでしまったり、
コードが冗長になってしまったりして、メンテナンスしにくくなるし、拡張性も失われます。
(まあ、そういう実装を全て否定するわけではありませんけどね。ケースバイケース^^;)



条件によってアルゴリズムの交換を行いたい場合や、
将来的にアルゴリズムが変更される可能性がある場合、Strategyパターンによる設計を試みるとよいだろう。
Strategyパターンでは、アルゴリズムをカプセル化して、振る舞いごとにクラスを作成するように考えます。
振る舞いごとに異なるクラスを複数用意することにより、メソッドに対して直接アルゴリズムを変更することなく、
条件に応じて適切なアルゴリズムに入れ替えることが可能となり、
機能と実装において、変更に対する柔軟性が高まります。



Strategyパターンの利点は、アルゴリズムそのものをカプセル化できることです。
また、カプセル化したアルゴリズムの実行にインターフェイスを使用することで、
アルゴリズムの拡張性と保守性を高めて再利用可能にします。
インターフェイスが提供するのは、あくまで操作(メソッド)の窓口のみです。
操作に対する実装は、アルゴリズムごとにクラスに分けてカプセル化するようにします。


たとえば、ここではラーメンを作ることを考えてみます。

ラーメンを作るために必要な共通の操作をインターフェイスとして抽出します。
この場合、「麺をゆでるメソッド()」、「丼にベースとなる秘伝のたれを加えるメソッド()」、
「丼に特製スープを加えるメソッド()」、「麺を丼に盛り付けるメソッド()」、
「トッピングを盛り付けるメソッド()」・・・などが考えられる。


例えば、ラーメンを作る際の共通操作である「麺をゆでるメソッド()」を実行するとき、
ゆでる時間や、ゆでる際のお湯と麺の割合などについて知る必要はありません。
5秒ほどでさっとゆで上げる「粉おとし」に始まり「ハリガネ」「バリ硬」「硬め」から、
「ふつう」「やわらかめ」に至るまで、麺の茹で上げ方のバリエーションは豊かです。


これは、Strategyパターンにおいて、「麺をゆでるメソッド()」がインターフェイスを意味し、
「粉おとし」「硬め」「やわらかめ」などの麺のゆで具合が、
インターフェイスで提供されるメソッドを実装している、アルゴリズムクラスに相当することがわかります。
このように関連する責務のまとまりを1つのクラスの振る舞いとして定義する
Strategyパターンのような手法を、「関心の分離(Separation Of Concerns)*1」と言い、
オブジェクト指向を扱う上での代表的なテクニックの1つとされています。
また、このSOC(関心の分離)はアスペクト指向への考え方にも繋がっていきます。



Strategyパターンは確かに、メンテナンス性や拡張性に優れた設計パターンですが、
いたずらにこのパターンを利用してしまうと、クラスの爆発(クラスの数が極端に多くなる)
が発生する恐れがあるので、それを避けたい場合には注意が必要です。
まあ「if文の爆発」よりは、何倍も何十倍もマシかと思いますが^^;


結構面倒くさかったけど作ったオマケ
Strategyパターン - VB.NET - 雑なサンプルソース

*1:構造化プログラミングで著名なDijkstraの説いた技法のひとつ。結合度を下げ凝集度を高めるテクニックの1つで、このテクニックの適用はメンテナンス性、再利用性を高める。