Rubyによるデザインパターン【Strategy】-取り替え可能パーツ群を戦略的に利用せよ-


# 概要

[Rubyによるデザインパターン](http://www.amazon.co.jp/gp/product/4894712857/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4894712857&linkCode=as2&tag=kidachi-22)第4章。
Strategy Pattern。

[Rubyによるデザインパターン5原則](http://qiita.com/kidachi_/items/4b63de9ad5a97726c50c)に則って理解する。


# どんなパターンか

抽象的な処理と具体的な処理を分離することで、
***変化に強い構造***を実現する。

→その目的は[Template Method](http://qiita.com/kidachi_/items/7c2a80bfc8a87a05051f)とほぼ同等

# Template Methodの課題

しかし、Template Methodには欠点がある。それは、
**継承をベースにしている**こと。

継承の欠点
http://qiita.com/kidachi_/items/4b63de9ad5a97726c50c#2-3

- スーパークラスの振る舞いの変更は、サブクラスの振る舞いを変える
- サブクラスはスーパークラスの中身を覗くことが出来る


そこでStrategyでは、
***変化しやすいコードの塊***を抽出し、
***全く別のクラス***に閉じ込める。

つまり、集約を使う。


## 異なるオブジェクトからアルゴリズムを引き出す


同じ目的を持った一群のオブジェクトをストラテジ（群）と捉える。
ストラテジ群は、そのすべてが全く同じインターフェイスを提供する。

コンテキスト（ストラテジを利用する側）は、各ストラテジを
**取り替え可能パーツ**として扱うことが出来るようになる。

**→関心の分離** を実現する。

# 実装

## コンテキスト（ストラテジを利用する側）

```ruby
class Report
  attr_reader :title, :text
  attr_accessor :formatter

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

  def output_report
    @formatter.output_report(self)
  end
end
```

## ストラテジ1（フォーマット：HTML）

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

## ストラテジ2（フォーマット：PlainText）

```ruby
class PlainTextFormatter
  def output_report(context)
    puts("*** #{context.title} ***")
    context.text.each do |line|
      puts(line)
    end
  end
end
```

## ストラテジの活用

```ruby
[9] pry(main)> report = Report.new(HTMLFormatter.new)
[10] pry(main)> report.output_report
<html>
  <head>
    <title>月次報告</title>
  </head>
  <body>
    <p>最高！</p>
    <p>順調</p>
    <p>普通</p>
  </body>
</html>

[12] pry(main)> report.formatter = PlainTextFormatter.new
[13] pry(main)> report.output_report
*** 月次報告 ***
最高！
順調
普通
```

Template MethodにしてもStrategyにしても、
アルゴリズムの切り替えを1箇所（から多くても数箇所）に集中できる。


# コンテキストとストラテジ間でのデータ共有

2者のデータ共有の方法としては2パターン存在する。

1. ストラテジへ、コンテキスト自体の参照（self）を渡す
- ストラテジ側で使う必要のあるコンテキスト側のデータは、全て引数として渡す

上の例では、1を用いているが、2のパターンも見てみる。

## ストラテジ側で使う必要のあるコンテキスト側のデータは、全て引数として渡す


```ruby
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
```

```ruby
class PlainTextFormatter
  def output_report(title, text)  # （オブジェクトではなく）値を受け取る
    puts("*** #{title} ***")
    text.each do |line|
      puts(line)
    end
  end
end
```

```ruby
[3] pry(main)> report = Report.new(PlainTextFormatter.new)
[4] pry(main)> report.output_report
*** 月次報告 ***
最高！
順調
普通
```


## メリット・デメリット

### 1. ストラテジへ、コンテキスト自体の参照（self）を渡す

メリット：データの流れがシンプルになる。
デメリット：コンテキストとストラテジの結合度が上がってしまう。

### 2. ストラテジ側で使う必要のあるコンテキスト側のデータは、全て引数として渡す

メリット：コンテキストとストラテジの分離は保てる
デメリット：渡すデータの数によっては、複雑で大量になる可能性がある。


→状況に応じて適切なものを利用すること。


# Procやブロックでストラテジを作る

ここまで、コンテキストとストラテジの関係を、
「オブジェクトとオブジェクト（の分離）」として捉えてきたが、
オブジェクトだけでなく、「コード」レベルでも、
分離し、持ち回すことが出来ないだろうか？

#### → Proc/ブロックで実現する。

※ Procやブロックの基礎は以下を参照。
[Ruby基礎] ブロックとProcをちゃんと理解する
http://qiita.com/kidachi_/items/15cfee9ec66804c3afd2


## コンテキスト2（ストラテジを利用する側）

コンテキスト初期化時に渡されてくるストラテジは、（クラスではなく）ブロックを期待する。

```ruby
class Report
  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(&formatter)  # 渡されてくるブロックをproc化
    @title = '月次報告'
    @text = ['最高！', '順調', '普通']
    @formatter = formatter
  end

  def output_report
    @formatter.call(self)  # Procをcall
  end
end
```

## ストラテジ3（フォーマット：PlainText、タイプ：ブロック）

```ruby
report = Report.new do |context|
  puts("*** #{context.title} ***")
  context.text.each do |line|
    puts(line)
  end
end
```

```ruby
[11] pry(main)> report.output_report
*** 月次報告 ***
最高！
順調
普通
```


## ストラテジ4（フォーマット：PlainText、タイプ：Proc）

```ruby
PLAIN_TEXT_FORMATTER = lambda { |context|
  puts("*** #{context.title} ***")
  context.text.each do |line|
    puts(line)
  end
}

report = Report.new(&PLAIN_TEXT_FORMATTER)  # procをブロック化して渡す

```

```ruby
[15] pry(main)> report.output_report
 *** 月次報告 ***
最高！
順調
普通
```


procやブロックを活用することで、手軽にストラテジを作ることが出来た。


# まとめ

Strategyパターンにより、


- 変わるもの（ストラテジ）と変わらないもの（コンテキスト）を分離して、変化に強い構造へ
- [継承より集約](http://qiita.com/kidachi_/items/4b63de9ad5a97726c50c#2-3)を実現
- ブロックによるストラテジも可能


# 以下へ続く

【Observer】-本日のニュースをお届けします-
http://qiita.com/kidachi_/items/ce18d2a926c558159689