Strategyパターンについて(デザインパターン)
【結論】
・Strategyパターンとは、デザインパターンの一つで、状況に応じてアルゴリズムを切り替える処理を記述する時に有効な実装手法
・ConcreteStrategy役に個々の具体的なアルゴリズムが記述されており、どれを使用するかはStrategy役が規定する
・アルゴリズムを容易に切り替えられるようになる為、新しいアルゴリズムの拡張や、既存のアルゴリズムの改修がしやすくなる
【目次】
【本題】
Strategyパターンについて
Strategyパターンは、デザインパターンの一種です。
Strategyは「戦略」という意味を持ちますが、プログラミングで言えばアルゴリズムがそれに相当します。
Strategyパターンにおいては、アルゴリズム(戦略)を状況に応じて切り替える実装方法になります。
構成
Strategyパターンは、Strategy(抽象戦略)とConcreteStrategy(具体戦略)とContext(文脈)の3つで構成されています。
ConcreteStrategy(具体戦略)に、具体的なアルゴリズムがプログラミングされています。
Strategy(抽象戦略)で、ConcreteStrategy(具体戦略)のどれを利用するのかを定めます。
Context(文脈)が、実際にStrategy(抽象戦略)を利用して、ConcreteStrategy(具体戦略)のアルゴリズムを実行します。
メリット
IF文などでメソッド内で条件分岐させる方法を採ると、アルゴリズムが増えるほどコード量が多くなり、可読性が落ちてしまいます。
また、アルゴリズムを追加・改良するにしても、他のアルゴリズムに影響を及ぼさない様に細心の注意を払う必要があります。
Strategyパターンを利用すれば、アルゴリズムを他の部分と分離することが出来ます。
これにより、簡単にアルゴリズムの追加・改良を行うことが可能になります。
サンプルコード
以下は、映画の各タイトルに3つの評価点が与えられており、それらをランキング形式で出力するコードです。
ランキングは「合計点」「最高点」「平均点」の3つのアルゴリズムから切り替えて出力できるようになっています。
コード
class Result attr_reader :scores attr_accessor :sort_type def initialize(scores, sort_type) @scores = scores @sort_type = sort_type end def output_ranking @sort_type.output_ranking(self) end end class TotalScoreSort def output_ranking(result) output = {} result.scores.each do |s| output[s[:title]] = [s[:judge1], s[:judge2], s[:judge3]].sum end puts '++++++ランキング(合計スコア)++++++' Hash[output.sort_by{ |_, v| -v }].each.with_index(1) do |o, i| puts "#{i}位:#{o[0]}(スコア:#{o[1]})" end end end class HighestScoreSort def output_ranking(result) output = {} result.scores.each do |s| output[s[:title]] = [s[:judge1], s[:judge2], s[:judge3]].max end puts '++++++ランキング(最高スコア)++++++' Hash[output.sort_by{ |_, v| -v }].each.with_index(1) do |o, i| puts "#{i}位:#{o[0]}(スコア:#{o[1]})" end end end class AverageScoreSort def output_ranking(result) output = {} result.scores.each do |s| output[s[:title]] = ([s[:judge1], s[:judge2], s[:judge3]].sum / 3.0).round(1) end puts '++++++ランキング(平均スコア)++++++' Hash[output.sort_by{ |_, v| -v }].each.with_index(1) do |o, i| puts "#{i}位:#{o[0]}(スコア:#{o[1]})" end end end
入力
scores = [ {title: 'ダークナイト', judge1: 8, judge2: 6, judge3: 7}, {title: 'トイストーリー4', judge1: 1, judge2: 3, judge3: 0}, {title: '容疑者Xの献身', judge1: 5, judge2: 4, judge3: 4}, {title: 'コマンドー', judge1: 2, judge2: 9, judge3: 1}, {title: 'インセプション', judge1: 7, judge2: 5, judge3: 6}, {title: 'LIFE!', judge1: 5, judge2: 6, judge3: 3} ] result = Result.new(scores, TotalScoreSort.new) result.output_ranking result = Result.new(scores, HighestScoreSort.new) result.output_ranking result = Result.new(scores, AverageScoreSort.new) result.output_ranking
出力
++++++ランキング(合計スコア)++++++ 1位:ダークナイト(スコア:21) 2位:インセプション(スコア:18) 3位:LIFE!(スコア:14) 4位:容疑者Xの献身(スコア:13) 5位:コマンドー(スコア:12) 6位:トイストーリー4(スコア:4) ++++++ランキング(最高スコア)++++++ 1位:コマンドー(スコア:9) 2位:ダークナイト(スコア:8) 3位:インセプション(スコア:7) 4位:LIFE!(スコア:6) 5位:容疑者Xの献身(スコア:5) 6位:トイストーリー4(スコア:3) ++++++ランキング(平均スコア)++++++ 1位:ダークナイト(スコア:7.0) 2位:インセプション(スコア:6.0) 3位:LIFE!(スコア:4.7) 4位:容疑者Xの献身(スコア:4.3) 5位:コマンドー(スコア:4.0) 6位:トイストーリー4(スコア:1.3)
参考情報
ストラテジ(Strategy) | Ruby デザインパターン | 酒と涙とRubyとRailsと