コードの品質向上のため、Rubyでデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめています。(毎週1つが目標です!)
前回の記事(テンプレートメソッドパターンのまとめ)はこちらです。
ysk-pro.hatenablog.com
この本で紹介されているサンプルコードをそのまま使うのは面白くないので、オリジナルのコードで説明しています。
今回はStrategyパターンについてまとめました。
Strategyパターンとは
アルゴリズムの変化する部分をクラスに閉じ込めて、アルゴリズムを実行する際はそのクラスに処理を委譲するパターンです。
アルゴリズムに多様性を持たせたい場合に利用するのはTemplateメソッドパターンと同じですが、Strategyパターンは継承を使わずに実現することができます。(Templateメソッドパターンは継承を使っており、継承にはサブクラスがスーパークラスに依存してしまうというデメリットがあります)
Strategyパターンの「Strategy(戦略)」は、アルゴリズムのことです。Strategyパターンという名前は、変化するアルゴリズム(=Strategy)を、クラスに閉じ込めてそのアルゴリズムを使うオブジェクトに引き渡すことから付けられました。
文章の説明よりも実際のコードを見た方が分かりやすいと思うので、以下のサンプルコードをご覧ください。
サンプルコード
今回はスポーツネタでいってみます。
様々なスポーツの練習内容を出力するプログラムを考えてみます。(プログラム中にあるようにゴルフとバドミントンが好きでよくやっています)
Strategyパターンを用いて書いた例がこちらです。
class Sport attr_accessor :sport, :before_practice def initialize(sport) @sport = sport # Golfクラス・Badmintonクラスのインスタンスが入る @before_practice = 'ストレッチをする' end def practice sport.practice(self) # 詳細を知っている別のクラス(Golf,Badminton)のメソッドに処理を委譲している end end class Golf def practice(sport) puts sport.before_practice puts 'アイアンの練習' puts 'ドライバーの練習' end end class Badminton def practice(sport) puts sport.before_practice puts 'スマッシュの練習' puts 'ヘアピンの練習' end end
Golfクラス・Badmintonクラスに、アルゴリズムの変化する部分を閉じ込めて、Sportクラスのpracticeメソッドの実行は、Golfクラス・Badmintonクラスに委譲しています。
こうすることによって、各スポーツの練習方法についての知識を、Sportクラスから分離することができます。
また、Sportクラスの情報(今回は スポーツ前にはストレッチをする という情報)をGolfクラス・Badmintonクラスに渡すために、practiceメソッドにSportクラス自身のインスタンスを引数として渡しています(before_practiceを引数として渡しても問題ありません)。
以下のように実行できます。
Sportクラスのインスタンス作成時の引数に、Golfクラス・Badmintonクラスのインスタンスを渡しています。
Sport.new(Golf.new).practice Sport.new(Badminton.new).practice
実行結果は、それぞれこのようになります。
ストレッチをする アイアンの練習 ドライバーの練習 ストレッチをする スマッシュの練習 ヘアピンの練習
また、Procオブジェクトを利用すると、同じ内容を次のように書くこともできます。
class Sport attr_accessor :sport, :before_practice def initialize(&sport) # 引数としてブロックを受け取るので、&(アンパサンド)をつけている @sport = sport @before_practice = 'ストレッチをする' end def practice sport.call(self) # Procオブジェクトはcallメソッドで実行する end end
こうすることで、アルゴリズムの変化する部分を閉じ込めるクラスを作る必要がなくなり(ブロックを疑似的なクラスとして扱っています)、よりシンプルにストラテジーパターンを利用することができるようになります。
次のように実行すると、先ほどと同じ実行結果となります。
Sport.new do |sport| puts sport.before_practice puts 'アイアンの練習' puts 'ドライバーの練習' end.practice Sport.new do |sport| puts sport.before_practice puts 'スマッシュの練習' puts 'ヘアピンの練習の練習' end.practice
アルゴリズムの変化する部分が小さい場合は、Procクラスを使った方法の方がシンプルにStrategyパターンを実現できます。
おわりに
ここまで読んでいただきありがとうございます。
GoF(ギャング オブ フォーの略でデザインパターンを広めた「オブジェクト指向における再利用のためのデザインパターン」の4人の著者のこと)によれば、「継承よりも委譲の方がより柔軟で好ましい」とあるので、Templateメソッドを検討する際には、合わせてStrategyパターンも検討してみるのが良さそうです。
Rubyによるデザインパターン の中では、レポートをHTML形式やプレーンテキスト形式で出力するという実際にありそうなサンプルコードを用いて説明されていて非常に分かりやすかったので、ご興味ある方は是非合わせてご覧ください。
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (66件) を見る
やっぱりブログを書くと、理解が深まっていいですね。先日僕が実務で書いたコードを、早速 Templateパターン or Strategyパターンを使ってリファクタリングしたくなりました。
次回は、オブザーバーパターンをまとめます。どんなパターンか楽しみです。
来週も頑張ります!
(追記)
オブザーバーパターンについてまとめました!
Strategyパターンと似たところもあり、合わせて読むことでそれぞれの理解が深まると思います。
是非合わせてご覧ください。
ysk-pro.hatenablog.com