k-tokitoh

2019-05-09

Rubyによるデザインパターン - decorator

[![Rubyによるデザインパターン](https://images-fe.ssl-images- amazon.com/images/I/41PNvUxHtgL.SL160.jpg)](http://www.amazon.co.jp/exec/obidos/ASIN/4894712857/hatena- blog-22/)

利用する場面

あるオブジェクトのメソッドについて、特定の場合に処理内容を付加したい。

あれこれ

サンプル

decorator に元のオブジェクトのメソッドを持たせる方法がいくつかある。

まずは decorator に元のオブジェクトを継承させる方法。

class TextProcessor
  def describe
    "this is a text processor."
  end

  def execute(text)
    text
  end
end

class TextDecorator < TextProcessor
  def initialize(original_processor)
    @original_processor = original_processor
  end
end

class Capitalizer < TextDecorator
  def execute(text)
    @original_processor.execute(text).upcase
  end
end

class Exclaimer < TextDecorator
  def execute(text)
    @original_processor.execute(text) << "!!!"
  end
end

text_processor = TextProcessor.new
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "hoge"

text_processor = Capitalizer.new(text_processor)
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "HOGE"

text_processor = Exclaimer.new(text_processor)
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "HOGE!!!"

元のオブジェクトの機能(describe メソッドとか)を損なうことなく、execute メソッドに処理を付加することができた。

次は decorator から元のオブジェクトに委譲する方法。 TextDecorator クラスだけ以下のように書き換える。

class TextDecorator
  def initialize(original_processor)
    @original_processor = original_processor
  end

  def describe
    @original_processor.describe
  end
end

この委譲を、forwardable モジュールをつかってよりシンプルに実現する。 いちいち中身が 1 行だけの委譲用メソッドを書かなくて済む。

class TextDecorator
  extend Forwardable
  def_delegators :@original_processor, :describe

  def initialize(original_processor)
    @original_processor = original_processor
  end
end

最後に、module をつかう方法。

これまでは「元のオブジェクトとほとんど同じ振る舞いをする decorator クラスを用意して、decorate するときは decorator クラスのインスタンスにどんどん decoretor モジュールを extend で付加していく」というやり方。

やたらと新しいインスタンスを使いたい」が叶わなくなってしまうのが欠点。

class TextProcessor
  def describe
    "this is a text processor."
  end

  def execute(text)
    text
  end
end

module Capitalizable
  def execute(text)
    super.upcase
  end
end

module Exclaimable
  def execute(text)
    super << "!!!"
  end
end

text_processor = TextProcessor.new
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "hoge"

text_processor.extend(Capitalizable)
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "HOGE"

text_processor.extend(Exclaimable)
p text_processor.describe # => "this is a text processor."
p text_processor.execute("hoge") # => "HOGE!!!"