GoFのデザインパターン(Design Pattern)のストラテジー(Strategy)のRubyコードを使った紹介記事です。
ストラテジーパターンは、例えば5ステップの中の3ステップが異なったAとBがあり、このAとBをスイッチしたい時に使えるパターンです。
ストラテジの構成
ストラテジは以下の3つのオブジェクトによって構成されます。
* コンテキスト(Context):ストラテジの利用者 * 抽象戦略(Strategy):同じ目的をもった一連のオブジェクトを抽象化したもの * 具象戦略(ConcreteStrategy):具体的なアルゴリズム
ストラテジのアイデアは、コンテキストが「委譲」によってアルゴリズムを交換できるようにすることです。委譲とは、ある機能を持つオブジェクトを生成してオブジェクトに処理を依頼することです。
ストラテジのメリット
* 使用するアルゴリズムに多様性を持たせることができる * コンテキストと戦略を分離することでデータも分離できる * 継承よりもストラテジを切り替えるのが楽
サンプルソース1
レポートをHTML形式とプレーンテキスト形式で作成するプログラムをサンプルとしてストラテジーパターンを解説します。サンプルの概要は次の通りです。
* Report(コンテキスト):レポートを表す * Formatter(抽象戦略):レポートの出力を抽象化したクラス * HTMLFormatter(具象戦略1):HTMLフォーマットでレポートを出力 * PlaneTextFormatter(具象戦略2):PlanTextフォーマットでレポートを出力
まずイメージしやすい、HTML形式で出力するHTMLFormatterクラスとPlaneTextFormatterクラス、そしてその2つのクラスのインターフェイスを規定するFormatterクラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
続いてレポートを表すReportクラスを作成します。このクラスにはformatterがあり、このformatterによって出力フォーマットを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
コーディングは以上です。では結果を確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Reportクラス内のformatterがレポートの出力を委譲されています。
上の結果からformatterをスイッチすれば出力形式(戦略)を変更させることができるのを確認できました。
ちなみに、ここにあるFormatterクラスはインターフェースを規定するだけのクラスなので、Rubyらしく書くなら不要です。(ダック・タイピング哲学)
サンプルソース2
先ほどのソースコードをProcオブジェクト(コードブロック)を使って置き換えると次のようになります。
Procオブジェクトは、コードのかたまりを保持するオブジェクトです。lambdaメソッドが良く使われます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
コーディングは以上です。では結果を確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
先ほどよりもRubyらしいコードで同様の結果を得ることができました。
このサンプルソースはGitHubにも置いています。
ストラテジの注意点
* コンテキストとストラテジ間のインターフェイスがストラテジの種類の増加を妨げないようにする * コンテキストの変更がストラテジに影響を与えないようにする
コンテキストからストラテジへのデータの渡し方は、(1) ストラテジメソッドを呼び出すときに適切なデータを渡す (2) コンテキストへの参照をストラテジに渡すといった方法があります。これを適切に選択してストラテジの種類を増やすことを阻害しないようにしてください。
Special Thanks
RubyでStrategyパターン - 暁 [stfuawsc]
RubyでStrategyパターン - tbpg’s programming memo
Amazon.co.jp: Rubyによるデザインパターン: Russ Olsen, ラス・オルセン, 小林 健一, 菅野 裕, 吉野 雅人, 山岸 夢人, 小島 努: 本
変更来歴
12/10 09:00 GitHubへのサンプルソースの設置。導入文の修正
12/11 00:00 書籍へのリンクをAmazon アフィリエイトに変更
12/16 18:05 ソースコードへの説明を追加
06/20 14:50 Ruby2.0.0対応、読みづらい部分を修正