Rubyによるデザインパターン【Template Method】-テンプレは準備した、あとはお好きに-


# 概要

[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)第3章。
Template Method Pattern。

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


# どんなパターンか

ソフトウェアには変化がつきものである。
その変化の度に、広範囲な修正が入っているようではとても対応できない。

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

そのための手段の一つがTemplate Method。

骨子は以下2種のクラスの利用。

- 骨格となるメソッドを持った抽象基底クラス（テンプレートクラス）
- 処理の詳細を詰める具象クラス（サブクラス）



# ひどいコード

あるレポートを様々なフォーマット（HTMLやPlainText）で出力するReportクラス

```ruby
class Report
  def initialize
    @title = '月次報告'
    @text = ['最高！', '順調', '普通']
  end

  def output_report(format)
    if format == :plain
      puts("*** #{@title} ***")
    elsif format == :html
      puts('<html>')
      puts('  <head>')
      puts("    <title>#{@title}</title>")
      puts('  </head>')
      puts('  <body>')
    else
      raise "Unknown format: #{format}"
    end

    @text.each do |line|
      if format == :plain
        puts(line)
      else
        puts("    <p>#{line}</p>")
      end
    end
    if format == :html
      puts('  </body>')
      puts('</html>')
    end
  end
end
```

フォーマットがHTMLなのかPlainTextなのかによって、
都度if分で処理を分けている。
フォーマットがさらに増えたらどうなるのか・・

ということで、解決策を考える。

# パターンの適用によるリファクタリング

### 変わるものと変わらないものを分離する

- 基本的な処理の流れ・テンプレート（変わらないもの）をフォーマット（変わるもの）に依存させるべきではない。
- 前者は抽象的な基底クラスに、後者は具象サブクラスに定義する。

### つまり

「基本的な処理の流れ」である、

1. 特定フォーマットに必要なヘッダ情報を出力する。
- タイトルを出力する。
- レポート本体（body）を出力する。
- フォーマットに要求される残りの要素を出力する。

これらを「テンプレ」として抽象基底クラスに定義し、残りはサブクラスへ切り出す。


# 実装

## 抽象基底クラス

```ruby
class Report
  def initialize
    @title = '月次報告'
    @text = ['最高！', '順調', '普通']
  end

  def output_report
    output_start
    output_title
    output_body_start
    @text.each do |line|
      output_line(line)
    end
    output_body_end
    output_end
  end

  def output_start
  end

  def output_title
    output_line(@title)
  end

  def output_body_start
  end

  def output_line(line)
    raise 'Called abstract method: output_line'
  end

  def output_body_end
  end

  def output_end
  end
end
```

output_start, output_title, output_body_start, output_body_end, output_endのように
***具象クラスによってオーバーライドできる（不要ならしなくてもよい）***メソッドのことを
***フックメソッド***と呼ぶ。

一方でoutput_lineは、直接呼び出すと例外を発生させる。つまり、
**具象クラスによるオーバーライドを強要**している。

→output_lineは、「各フォーマットごとに処理が異なる＝より変化しやすい処理」
という想定があるため。

## 具象サブクラス１（フォーマット：HTML）

```ruby
class HTMLReport < Report
  def output_start
    puts('<html>')
  end

  def output_title
    puts('  <head>')
    puts("    <title>#{@title}</title>")
    puts('  </head>')
  end

  def output_body_start
    puts('  <body>')
  end

  def output_line(line)
    puts("    <p>#{line}</p>")
  end

  def output_body_end
    puts('  </body>')
  end

  def output_end
    puts('</html>')
  end
end
```

## 具象サブクラス２（フォーマット：PlainText）

```ruby
class PlainTextReport < Report
  def output_title
    puts("*** #{@title} ***")
  end

  def output_line(line)
    puts(line)
  end
end
```

## クラスの利用

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

[19] pry(main)> report = PlainTextReport.new
[20] pry(main)> report.output_report
*** 月次報告 ***
最高！
順調
普通
```

# まとめ

TemplateMethodパターンにより、

### 変わるもの（具象サブクラス）と変わらないもの（テンプレート）を分離して、変化に強い構造へ。

# 以下へ続く

【Strategy】-取り替え可能パーツ群を戦略的に利用せよ-
http://qiita.com/kidachi_/items/02f8f6df955ba1d0e93b