Shred IT!!!!

IT全般について試したこと・勉強したことを綴ったり、趣味について語るブログ

3. Rubyによるデザインパターン【Builder】

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

3回目のデザインパターンは Builder(ビルダー) パターンの記事を書いてみる。

Template Method と Strategy のいいところを使ったパターンだと思う。


おなじみ、下記ソウルを忘れないこと。
参考:programming - Rubyによるデザインパターン5原則 - Qiita

  • 変わるものを変わらないものから分離する
  • プログラムはインターフェイスに対して行う(実装に対して行わない)
  • 継承より集約
  • 委譲、委譲、委譲
  • 必要になるまで作るな(YAGNI

調査

Wikiで調べてみる。
参考:Builder パターン - Wikipedia

オブジェクトの生成過程を抽象化することによって、動的なオブジェクトの生成を可能にする。

Wikiの図の説明だが、Director は Builder を内包しており、buildPart メソッドで処理を Builder へ委譲している。
Builder インターフェースの実装を ConcreteBuilder で行い、ConcreteBuilder は buildPart メソッドによって Product を生み出し、getResult メソッドで Product を返却する。

簡単に言うと、Director が Builder に Product を作らせている。
Builder をすげかえることで、出来上がる Product を変えられる。

また Builder における生成過程の抽象化を Director が行っているため、Director をすげかえることでも、生成過程の異なる Product を生成させることができる。

Template Method や Strategy との違いは、Builer パターンはオブジェクトの生成を目的としている。


概念

なるべくイメージしやすいことに例えて、Builder パターンを考えてみる。

魔王(Director)
  魔物を作る(悪い博士に委譲)
    悪い博士に魔物を開発させる
    悪い博士に魔物を武器を持たせる
    悪い博士に魔物を連れてこさせる

悪い博士インターフェース(Builder)
  魔物を開発する
  魔物に武器を持たせる
  魔物を連れて行く

ゴブリン博士(ConcreteBuiler) < 悪い博士
  魔物を開発する
    ゴブリンを開発する
  魔物に武器を持たせる
    ゴブリンに武器を持たせる
  魔物を連れて行く
    ゴブリンを連れて行く

オーク博士 < 悪い博士
  ...

魔物(Product)
  名前
  身長
  体重
  ...

ゴブリン < 魔物
オーク < 魔物

悪の大王(Director)は魔物(Product)を作りたいが、魔物を作るのは複雑なため、悪の大王には魔物を作ることができない(インスタンス生成)。
そこで悪い博士(Builder)を連れて来て、魔物を作らせている。
悪の大王は悪い博士に指示を出し、魔物の生成は悪い博士が行っている。

ゴブリン博士ならゴブリンを作れ、オーク博士ならオーク博士を作れる。
悪の大王は魔物を直接作ることはできないが、悪い博士達に指示を出して、目的の魔物を作らせることができる。


実装

# -*- coding: utf-8 -*-
class Maou
  def set_doctor(doctor)
    @doctor = doctor
  end
  def create_monster
    @doctor.create_monster
    @doctor.equip_weapon_to_monster
    @doctor.get_monster
  end
end

class Doctor
  def create_monster;end
  def equip_weapon_to_monster;end
  def get_monster;end
end

class GoblinDoctor < Doctor
  def create_monster
    @monster = Goblin.new
  end
  def equip_weapon_to_monster
    @monster.weapon = "knife"
  end
  def get_monster
    @monster
  end
end

class Monster
  attr_accessor :weapon
end
class Goblin < Monster;end

maou = Maou.new
maou.set_doctor(GoblinDoctor.new)
goblin = maou.create_monster
p goblin
#<Goblin:0x007f95a210c540 @weapon="knife">

Maou クラスは set_doctor メソッドによって、 Doctor クラスを内包する。 Maou クラスは create_monster メソッドによって、Monster のインスタンス生成に関する過程を抽象化しつつ、Doctor へ Monster のインスタンス生成処理を委譲している。

上記の例で、GoblinDoctor クラスでは、Goblin のインスタンス生成過程が実装されているので、create_monster メソッドインスタンス生成、equip_weapon_to_monster メソッドでパラメータ設定、get_monster メソッドインスタンス返却を行っている。

Maou(Director)がDoctor(Builder)にMonster(Product)を作らせる、Builderパターンの実装になっている。


まとめ

オブジェクト生成に特化した Builer パターン。 正直、今までの業務でこのパターンで実装した記憶がない。

使いどころとしては、あるクラスのインスタンス生成や状態が複雑な場合に利用するといい。
ただ、業務上で利用する場合の具体例が出てこなくて悔しい・・・。

記事にしていて思ったのは Director の可能性。
Director を複数実装することでもメリットはあると思う。(Director を複数実装している記事をあまり見かけない)

魔王の例だと、魔王1は武器のみを持たせる方針だが、魔王2は武器と防具を持たせる方針、とか生成過程の差分を Director で吸収させる目的で Builder パターンを使うのはありだと思った。

例えば、完璧な車インスタンスが欲しい場合と、タイヤがない車インスタンスが欲しい場合、
完璧な車作る職人とタイヤがない車作る職人をそれぞれ Builder として実装するのはイケテナイ。

もし、この車がスポーツカー・トラック・オープンカーなど、種類が増えたときに Builder 側でそれらを吸収しようとすると、車の種類が追加される度に、2人の職人を追加していくことになってしまう・・・

この場合、タイヤがない車作る職人は必要なくて、完璧な車作る職人に出す指示、つまり、Director の生成過程でタイヤの取り付けをするかしないかで、実装を分けた方が DRY な実装になる。

おさらい

  • Director が Builder に Product を作らせている。
  • Director にも目を向けろ
次回

次は生成つながりで、 Factory Method パターンでも記事にしようと思う。


下記サイトを参考にさせて頂きました。

ビルダー Ruby 2.0.0 デザインパターン速攻習得[Builder][Design Pattern] - 酒と泪とRubyとRailsと