Rubyデザインパターン 8日目 : Decorator

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第8日目はDecoratorです。(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=morizyun00-22)

![スクリーンショット 2015-07-27 11.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/80075/d68a4010-c177-4ef5-2f2e-899a76c76cb6.png)


# **<i class="fa fa-pencil"></i>　8日目 Decorator**

今回のパターンは、**オブジェクトにレイヤ状に機能を追加できる**ようにするパターンです。

アイスに例えると、みなさんもアイス屋さんでアイスを買うときにいろんな味を重ねたりした事があると思います。チョコレート味の上に抹茶味を載せたり、またあるときは、ストロベリー味の上にミント味を載せ、またその上にピスタチオ味をのせたりと。

そういったように、機能同士を柔軟に組み合わせる事ができるようなパターンになります。


# **<i class="fa fa-code"></i> Decorator サンプルコード**
```rb
class SimpleWriter
  def initialize(path)
    @file = File.open(path, "w")
  end

  def write_line(line)
    @file.print(line)
    @file.print("\n")
  end

  def pos
    @file.pos
  end

  def rewind
    @file.rewind
  end

  def close
    @file.close
  end
end

class WriterDecorator
  def initialize(real_writer)
    @real_writer = real_writer
  end

  def write_line(line)
    @real_writer.write_line
  end

  def pos
    @real_writer.pos
  end

  def rewind
    @real_writer.rewind
  end

  def close
    @real_writer.close
  end
end
```

最も基本的なクラスである`SimpleWriter`クラスを作ります。
そして次にデコレーションをするための`WriterDecorator`クラスを作成します。
構成としてはほぼ同じインターフェイスを提供しています。

ここで単なるテキストを出力するSimpleWriterの機能を拡張するためにのコンポーネントを作成します。

```rb

class NumberingWriter < WriterDecorator
  def initialize(real_writer)
    super(real_writer)
    @line_number = 1
  end

  def write_line(line)
    @real_writer.write_line("#{@line_number}: #{line}")
    @line_number += 1
  end
end

class TimeStampingWriter < WriterDecorator
  def write_line(line)
    @real_writer.write_line("#{Time.now}: #{line}")
  end
end
```
`NumberingWriter`クラス、`TimeStampingWriter`クラスのインターフェイスはともに基礎となる`SimpleWriter`とコアの部分はほとんど変わりません。
どれも共通して`write_line`メソッドを持っています。

```rb
writer = NumberingWriter.new(TimeStampingWriter.new(SimpleWriter.new('final.txt'))) 
writer.write_line('気力にかくるなかりしか')
```
プログラム実行の際はこういった形で、委譲に次ぐ委譲形式になります。
タイムスタンプだけ表示したければ下記のようにすればいいです。

```rb
writer = TimeStampingWriter.new(SimpleWriter.new('hoge.txt'))
writer.write_line('不精にわたるなかりしか')
```

こういったように、欲しい機能だけ委譲形式で渡せばいいのです。
なので、**すべてのコンポーネントでインターフェイスを揃えておく必要**があります。
つまり、
1. ConcreteComponentクラス(`SimpleWriter`)
2. Decoratorクラス(`WriterDecorator`)
3. Componentクラス(`NumberingWriter`,`TimeStampingWriter`)

この**すべてのクラスが`write_line`メソッドを持ち合わせている事が必要だ**ということです。
以上がDecoratorパターンの基本形となります。


## **<i class="fa fa-diamond"></i> Rubyらしくしてみましょう！**

お待たせしました。Ruby祭りの時間です〜〜〜
上記で実現したことをRuby特有の形にしていきましょう。

### <i class="fa fa-pencil"></i> パターン 1(特異メソッド形式)
```rb
w = SimpleWriter.new('out')

class << w
  alias old_write_line write_line
  def write_line(line)
    old_write_line("#{Time.new}: #{line}")
  end
end

w.old_write_line("old!") # 文章のみ "old!"
w.write_line("new!")     # 時刻も出力される "2015-08-04 14:14:56 +0900: new!"
```
特異メソッドを使うと、わざわざ機能拡張のためのクラスを作らなくて済みます。
`alias`を呼び出したところでは`SimpleWriter`オブジェクトの元々持っていた`write_line`が呼び出されて`old_write_line`がエイリアスに設定されます。
`alias`が先に書いてないと`old_write_line`が定義できず、今回の結果にならないので注意しましょう。


### <i class="fa fa-pencil"></i> パターン 2(モジュール形式) 

```rb

module TimeStampingWriter
  def write_line(line)
    super("#{Time.new}: #{line}")
  end
end

module NumeberingWriter
  attr_reader :line_number

  def write_line(line)
    @line_number = 1 unless @line_number
    super("#{line_number}: #{line}")
    @line_number += 1
  end
end

w = SimpleWriter.new('out')
w.extend(NumberingWriter)
w.extend(TimeStampingWriter)

w.write_line('hello')
```
機能をモジュール形式にして追加するやり方です。
ただしこの方法だと欠点が一つあり、後でmoduleを追い出す方法がありません。

どちらの方法もメリットデメリットがありそうですので、導入の際にはしっかり検討したほうがいいでしょう <i class="fa fa-lightbulb-o"></i>

### <i class="fa fa-diamond"></i> Rubyでの実例

```rb
module Logging
  def log(message)
    puts message
  end

  def log_with_timestamp(message)
    log_without_timestamp("#{Time.now}: #{message}")
  end

  alias_method :log_without_timestamp, :log
  alias_method :log, :log_with_timestamp
end

include Logging

log('hello') # 2015-08-04 14:45:17 +0900: hello
log_without_timestamp('hello') # hello
```
`alias_method`を使うことによってタイムスタンプありなしそれぞれのメソッドを持ったモジュールを作成することができます

### <i class="fa fa-road"></i> Railsでの実例
```rb
module Logging
  def log(message)
    puts message
  end

  def log_with_timestamp(message)
    log_without_timestamp("#{Time.now}: #{message}")
  end

  alias_method_chain :log, :timestamp
end

include Logging
log('hello')
```
RailsのActiveSupportライブラリを使用すると、`alias_method_chain`メソッドを使用することができます。
こうすることによって、非常にシンプルに拡張メソッドとエイリアスを作ることができますね！
こちらはあくまでRailsでしか使えないので、その点だけは注意です！
（ちなみに素のRubyでどうしても事前にActivesupportCoreExtensionをロードしておけばできます)

```rb
require 'active_support'
require 'active_support/core_ext'
```



# **<i class="fa fa-star"></i>　まとめ**

Decoratorパターンは、基礎となる一つのクラスと、それと組になる一つのデコレータを作成します。
コンポーネントはその基礎となるクラスやデコレータと同じインターフェイスを持っており、コンポーネントに多様性を持たせることによって基礎となるクラスの機能を拡張します。

Decoratorの特に便利な部分は、すべてのクラスでインターフェイスが統一してあるので、設計した後で心配せずにどんどん機能を追加できるところでしょう。

このパターンを候補に入れておくと、**似たようなクラスをたくさん作りたいからといってどんどん継承関係を構築していったら**、いつの間にかコントロール不可能なプログラムができあがっていた、という悲惨な事態を防げるかもしれません。

## 参照
* http://qiita.com/seri_k/items/4818af527bd0e94cc860
* http://www.techscore.com/blog/2013/02/27/rails-alias_method_chain-%E6%97%A2%E5%AD%98%E3%81%AE%E5%87%A6%E7%90%86%E3%82%92%E4%BF%AE%E6%AD%A3%E3%81%99%E3%82%8B%E5%B8%B8%E5%A5%97%E6%89%8B%E6%AE%B5/