Ruby Strategyパターン


はじめに

構造がきれいな設計のために、デザインパターンを再学習しています。
今回はStrategyパターンを扱います。

ちなみに、きれいなコードとはデザインパターンのような構造に関するものと、
リーダブルコードに書いてあるような、読みやすコードの記述方法の2つからなると考えています。

環境

Ruby 2.3.0

概要

振る舞いに関するパターンのひとつで、アルゴリズムを交換できるよう実装します。
「別々のオブジェクトにアルゴリズムを引き出す」テクニックとのこと。
また、Strategyとは英語で「戦略」という意味です。

Strategyパターンを適用するとifをなくすことができます。

コンテキストにもよるので、ケースバイケースですが、
ifなどで分岐してしまうと、複雑になり、可読性も下がります。
理想はifができるだけ少ないプログラムです。

コード1

コード1はクラスの継承による実装です。
継承を使用すると、他に継承できなくなる(多重継承はできない)のと、
アルゴリズムごとにクラスを用意する必要があることがイマイチな気がします。

Strategy.rb
#継承(抽象)クラスを使用したstrategy
class AbstractFormatter
  def output
    raise "Abstract Method"
  end
end

class TextFormatter < AbstractFormatter
  def output
    puts "text"
  end
end

class HtmlFormatter < AbstractFormatter
  def output
    puts "html"
  end
end

class XMLFormatter < AbstractFormatter
  def output
    puts "XML"
  end
end

text = TextFormatter.new
text.output # => text

html = HtmlFormatter.new
html.output # => html

xml = XMLFormatter.new
xml.output # => xml

コード2

コード2はProcオブジェクトを使用した実装で、
よりRubyらしく、アルゴリズムの切り替えも実装できています。

Strategy_lambda.rb
#lambdaを使用したstrategy
class Formatter
  def initialize(&formatter)
    @formatter = formatter
  end

  def switch_formatter(&formatter)
    @formatter = formatter
  end

  def output
    @formatter.call
  end
end

text = -> { puts "text" }
html = -> { puts "html" }
xml = -> { puts "xml" }

formatter = Formatter.new &html
formatter.output # => text

formatter.switch_formatter &text
formatter.output # => html

formatter.switch_formatter &xml
formatter.output # => xml

参考