Rubyによるデザインパターンまとめ : builder
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (66件) を見る
利用する場面
オブジェクトを構成するために大量のコードが必要な場合。
方法
オブジェクトを構成するためのコードをオブジェクト自体から分離し、builderクラスにもたせる。
あれこれ
他のパターンとの違いについて
factoryは、決まりきった一式のオブジェクトを生成するときに適用する。(こっちのオブジェクトはa,b,cというインスタンスをつくり、こっちのオブジェクトはx,y,zというインスタンスをつくる、といった具合に。)
builderは、様々なパターンのオブジェクトを煩雑なオプションによって生成するときに適用する。(こっちのオブジェクトはa,b,cというインスタンスをつくり、こっちはa',b'',c,dをつくる、といった具合に。)
以下の例えが大いに理解を助けてくれたと感じた。
「ハーディーズ(Hardee’s: 米国のレストランチェーン)で何か注文するところを考えます。たとえば『ビッグハーディ』を頼めば、店の人は何も聞かずに持ってきます。これだとsimple factoryの例になります。しかしたとえば、少し凝ったサブウェイ風のが欲しい場合、ハンバーガーの作り方にはいろいろなオプションが考えられます(どんなバンズにするか、ソースの種類は何がいいか、チーズはどれにするか、など)。このような場合にはbuilderパターンが助けに来てくれるでしょう。」(参考:[保存版]人間が読んで理解できるデザインパターン解説#1: 作成系(翻訳))
また、書籍に記載されたサンプルコードでいえばだが、以下の点も違いとして見受けられた。
- factoryの場合には、Hogeインスタンスが(インスタンス変数として)HogeFactoryインスタンスを持つ。
- builderの場合には、HogeBuilderインスタンスが(インスタンス変数として)Hogeインスタンスを持つ。
まあこれは本質的な違いではなく、サンプルコードがたまたまそうなったというだけの話で、どちらの方法もありうるのではないかと思う。
サンプル
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>
おおー。たのしい。