第4章 アルゴリズムを交換する: Strategy part1
Ruby
Published: 2019-06-19

「Rubyによるデザインパターン」の読書メモです。

時々、個人の見解入りです。

継承に基づいた設計は、いくつかの深刻な不利益をもたらします。たとえどんなに注意深くコードを設計しても、サブクラスはそのスーパークラスに依存してしまいます。

そこで、「委譲」を使ってコードを書き直してみます。

Strategy を適用する (version1)

class Formatter
  def output_report(title, text)
    raise 'Abstract method called'
  end
end

class HTMLFormatter < Formatter
  def output_report(title, text)
    puts '<html>'
    puts "<head><title>#{@title}</title></head>"
    puts('<body>')
    text.each do |line|
      puts("<p>#{line}</p>")
    end
    puts('</body>')
    puts '</html>'
  end
end

class PlainTextFormatter < Formatter

  def output_report(title, text)
    puts("*****#{title}*****")
    text.each do |line|
      puts(line)
    end
  end
end

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(formatter)
    @title = '月次報告'
    @text = ['順調', '最高']
    @formatter = formatter
  end

  def output_report
    @formatter.output_report(@title, @text)
  end
end

report = Report.new(HTMLFormatter.new)
report.output_report

report.formatter = PlainTextFormatter.new
report.output_report

formatter を外から注入しているので、依存性の注入と言えそうです。

コンテキストとストラテジの間でのデータを共有する方法

  • コンテキストがストラテジオブジェクトのメソッドを呼び出すときに引数で渡す
  • ストラテジへと渡されたコンテキスト自身の参照を使う
    • データの流れが単純化されますが、コンテキストとストラテジ間の結合度を上げてしまうことにもなります。

Strategy を適用する (version2)

Formatter クラスはインターフェースを規定するためにしか、存在しません。

Ruby のダックタイピングの哲学に反するため、削除します。

class HTMLFormatter
  def output_report(title, text)
    puts '<html>'
    puts "<head><title>#{@title}</title></head>"
    puts('<body>')
    text.each do |line|
      puts("<p>#{line}</p>")
    end
    puts('</body>')
    puts '</html>'
  end
end

class PlainTextFormatter

  def output_report(title, text)
    puts("*****#{title}*****")
    text.each do |line|
      puts(line)
    end
  end
end

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(formatter)
    @title = '月次報告'
    @text = ['順調', '最高']
    @formatter = formatter
  end

  def output_report
    @formatter.output_report(@title, @text)
  end
end

report = Report.new(HTMLFormatter.new)
report.output_report

report.formatter = PlainTextFormatter.new
report.output_report