k-tokitoh

2019-05-14

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

[![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/)

利用する場面

オブジェクトを構成するために大量のコードが必要な場合。

方法

オブジェクトを構成するためのコードをオブジェクト自体から分離し、builder クラスにもたせる。

あれこれ

他のパターンとの違いについて

factory は、決まりきった一式のオブジェクトを生成するときに適用する。(こっちのオブジェクトは a,b,c というインスタンスをつくる、といった具合に。)

builder は、様々なパターンのオブジェクトを煩雑なオプションによって生成するときに適用する。(こっちのオブジェクトは a,b,c というインスタンスをつくり、こっちは a’,b”,c,d をつくる、といった具合に。)

以下の例えが大いに理解を助けてくれたと感じた。

「ハーディーズ(Hardee’s: 米国のレストランチェーン)で何か注文するところを考えます。たとえば『ビッグハーディ』を頼めば、店の人は何も聞かずに持ってきます。これだと simple factory の例になります。しかしたとえば、少し凝ったサブウェイ風のが欲しい場合、[保存版]人間が読んで理解できるデザインパターン解説#1: 作成系(翻訳))

また、書籍に記載されたサンプルコードでいえばだが、以下の点も違いとして見受けられた。

まあこれは本質的な違いではなく、サンプルコードがたまたまそうなったというだけの話で、どちらの方法もありうるのではないかと思う。

サンプル

class Jiro
  attr_accessor :abura, :yasai, :karame
end

class JiroBuilder
  attr_reader :jiro

  def initialize
    @jiro = Jiro.new
  end

  def add_abura(order = :hutsuu)
    @jiro.abura = Abura.new(order)
  end

  def add_yasai(order = :hutsuu)
    @jiro.yasai = Yasai.new(order)
  end

  def add_karame(order = :hutsuu)
    @jiro.karame = Karame.new(order)
  end
end

class Topping
  def initialize(order)
    @volume =
      case order
      when :mashimashi
        300
      when :mashi
        100
      when :sukuname
        20
      else :hutsuu
        50
      end
  end
end

class Abura < Topping  ; end
class Yasai < Topping  ; end
class Karame < Topping ; end

jb = JiroBuilder.new
jb.add_abura
jb.add_yasai(:mashimashi)
jb.add_karame(:sukuname)
jiro = jb.jiro
p jiro.abura  # => #<Abura:0x007fafea957120 @volume=50>
p jiro.yasai  # => #<Yasai:0x007fafea9570a8 @volume=300>
p jiro.karame # => #<Karame:0x007fafea957080 @volume=20>

method_missing を利用した magic method というものをやってみる。

JiroBuilder クラスに以下のメソッドを追加する。

def method_missing(name, *args)
  words = name.to_s.split("_")
  words.each_slice(2) do |topping, order|
    send("add_#{topping}", order.to_sym)
  end
end

すると、以下のようなことができる。

jb = JiroBuilder.new
jb.abura_mashi_yasai_sukuname_karame_mashimashi
jiro = jb.jiro
p jiro.abura  # => #<Abura:0x007f8fe605d6d8 @volume=100>
p jiro.yasai  # => #<Yasai:0x007f8fe605d610 @volume=20>
p jiro.karame # => #<Karame:0x007f8fe605d570 @volume=300>

おおー。たのしい。