Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

Ruby | Strategy Pattern

概要

Strategy Pattern

詳細

Strategy Patternは、委譲を利用してプログラム内のアルゴリズムの一部を交換可能にします。

サンプル仕様

株式会社HogeHogeの社内レポートを作成する。
レポートはHTMLとMarkdownの2種類です。
(HTMLはMarkdownから出せばいいような気もしますが気にしない。)

・Strategy適用その1(クラスによる抽出)
Strategyパターンを適用するために、文書のフォーマットに関わる処理をクラスに抽出します。
フォーマットごとに別のクラスを作成し、同じ振る舞いを持たせます。
これにより、レポートの抽出処理・フォーマット処理を別々のクラスに責務分割でき、
さらにフォーマットの種類が増えてもフォーマットクラスを増やすことで簡単に拡張できます。

・Strategy適用その2(Procによる抽出)
クラスを定義する代わりにProcを渡します。
クラスによる抽出との使い分けについては、各クラスのメソッドが一つで済むようなシンプルなケースで有効になります。

サンプル修正前

require 'test_toolbox'

module HogeHogeInc
  HTML_FORMATTER = proc do |title, contents|
    <<-EOS
<html>
<head>
  <title>#{title}</title>
</head>
<body>
#{contents}
</body>
</html>
    EOS
  end

  MARKDOWN_FORMATTER = proc do |title, contents|
    <<-EOS
# #{@title}
## Report
#{@contents}
    EOS
  end

  class Report
    attr_reader :formatter, :title, :contents
    def initialize(title, contents, formatter)
      @formatter,@title, @contents = formatter, title, contents
    end

    def output_report
      @formatter.call(@title, @contents)
    end
  end
end

html_report = HogeHogeInc::Report.new("html title", "html contents", HogeHogeInc::HTML_FORMATTER)
puts html_report.output_report

dp_line __LINE__

markdown_report = HogeHogeInc::Report.new("markdown title", "markdown contents", HogeHogeInc::MARKDOWN_FORMATTER)
puts markdown_report.output_report

__END__
dp_line は tbpgr_utils gem の機能
詳しくは
https://github.com/tbpgr/tbpgr_utils
を参照。

出力

<html>
<head>
  <title>html title</title>
</head>
<body>
html contents
</body>
</html>
--------------------|filename=|line=40|--------------------
# 
## Report

サンプルパターン適用その1

require 'test_toolbox'

module HogeHogeInc
  class HtmlFormatter
    def initialize(title, contents)
      @title, @contents = title, contents
    end

    def output_report
      <<-EOS
<html>
<head>
  <title>#{@title}</title>
</head>
<body>
#{@contents}
</body>
</html>
      EOS
    end
  end

  class MarkdownFormatter
    def initialize(title, contents)
      @title, @contents = title, contents
    end

    def output_report
      <<-EOS
# #{@title}
## Report
#{@contents}
      EOS
    end
  end

  class Report

    attr_reader :formatter
    def initialize(formatter)
      @formatter = formatter
    end

    def output_report
      @formatter.output_report
    end
  end
end

html_report = HogeHogeInc::Report.new(HogeHogeInc::HtmlFormatter.new("html title", "html contents"))
puts html_report.output_report

dp_line __LINE__

markdown_report = HogeHogeInc::Report.new(HogeHogeInc::MarkdownFormatter.new("markdown title", "markdown contents"))
puts markdown_report.output_report

__END__
dp_line は tbpgr_utils gem の機能
詳しくは
https://github.com/tbpgr/tbpgr_utils
を参照。

出力

<html>
<head>
  <title>html title</title>
</head>
<body>
html contents
</body>
</html>
--------------------|filename=|line=53|--------------------
# markdown title
## Report
markdown contents

サンプルパターン適用その2

require 'test_toolbox'

module HogeHogeInc
  HTML_FORMATTER = proc do |title, contents|
    <<-EOS
<html>
<head>
  <title>#{title}</title>
</head>
<body>
#{contents}
</body>
</html>
    EOS
  end

  MARKDOWN_FORMATTER = proc do |title, contents|
    <<-EOS
# #{title}
## Report
#{contents}
    EOS
  end

  class Report
    attr_reader :formatter, :title, :contents
    def initialize(title, contents, formatter)
      @formatter,@title, @contents = formatter, title, contents
    end

    def output_report
      @formatter.call(@title, @contents)
    end
  end
end

html_report = HogeHogeInc::Report.new("html title", "html contents", HogeHogeInc::HTML_FORMATTER)
puts html_report.output_report

dp_line __LINE__

markdown_report = HogeHogeInc::Report.new("markdown title", "markdown contents", HogeHogeInc::MARKDOWN_FORMATTER)
puts markdown_report.output_report

__END__
dp_line は tbpgr_utils gem の機能
詳しくは
https://github.com/tbpgr/tbpgr_utils
を参照。

出力

<html>
<head>
  <title>html title</title>
</head>
<body>
html contents
</body>
</html>
--------------------|filename=|line=40|--------------------
# markdown title
## Report
markdown contents