Rubyデザインパターン - 生成に関するパターン

![](https://ruby.tw/logo.gif)

## Singleton
### 概要
そのクラスのインスタンスが一つしかないことを保証する
オブジェクトを外部から直接生成させることを防ぎ、クラス自体に同時に生成できるオブジェクトの数を管理させる

### 使い道
常に同じオブジェクトをプログラム内で扱いたい
DBのコネクション数を制限したい

### 例

```
require 'singleton'
class SingletonObject
  # instanceメソッドが定義され、newメソッドがprivateに設定される
  include Singleton
  attr_accessor :counter, :name
  def initialize
    @counter = 0
    @name = "Singleton"
  end
end

obj1 = SingletonObject.instance
obj1.counter += 1
p "--- #{obj1.name} ---"
p obj1.counter

obj2 = SingletonObject.instance
obj2.counter += 1
obj2.name = "UpdatedName"
p "--- #{obj1.name} ---"
p obj2.counter
p obj1.equal?(obj2)

# "--- Singleton ---"
# 1
# "--- UpdatedName ---"
# 2
# true
```
## Builder
### 概要
複雑なインスタンスの生成を他のオブジェクト(Director)にまかせる
Builderが作業を担当
Directorが作業過程を担当

### 例

```ruby
# ConcreteBuilder
class SaltWater
  attr_accessor :water, :salt, :color
  def initialize(water, salt, color)
    @water = water
    @salt = salt
    @color = color
  end

  def add_material(salt_amount)
    @salt += salt_amount
  end

  def update_color
    @color = "blue"
  end
end

# ConcreteBuilder
class SugarWater
  attr_accessor :water, :sugar, :color
  def initialize(water, sugar, color)
    @water = water
    @sugar = sugar
    @color = color
  end

  def add_material(sugar_amount)
    @sugar += sugar_amount
  end

  def update_color
    @color = "red"
  end
end

# Builder
class WaterWithMaterialBuilder
  attr_reader :water_with_material
  def initialize(class_name)
    @water_with_material = class_name.new(0,0, "white")
  end

  def add_material(material_amount)
    @water_with_material.add_material(material_amount)
  end

  def add_water(water_amount)
    @water_with_material.water += water_amount
  end

  def update_color
    @water_with_material.update_color
  end
  
  def result
    @water_with_material
  end
end

# Director
class Director
  attr_reader :builder
  def initialize(builder)
    @builder = builder
  end

  def magic
    @builder.add_water(150)
    @builder.add_material(90)
    @builder.add_material(35)
    @builder.update_color()
  end
end

builder = WaterWithMaterialBuilder.new(SugarWater)
director = Director.new(builder)
director.magic
p builder.result
p director.builder.water_with_material.color
# #<SugarWater:0x007fed55824b10 @water=150, @sugar=125, @color="red">
# "red"

builder = WaterWithMaterialBuilder.new(SaltWater)
director = Director.new(builder)
director.magic
p builder.result
p director.builder.water_with_material.color
# #<SaltWater:0x007fed5582fe98 @water=150, @salt=125, @color="blue">
# "blue"
```
## Abstract Factory
### 概要
関連し合うオブジェクトの集まりを生成することができる
矛盾のないオブジェクトの生成を行うため

### 例

```rb
class DinnaerFactory
  def initialize
  end
  def createDinner
    return [createAppetizer,createMain]
  end
  def createMain
  end
  def createAppetizer
  end
end

class Soup
  def initialize
  end
end

class Rice
  def initialize
  end
end

class Salad
  def initialize
  end
end

class Pizza
  def initialize
  end
end

class JapaneseFactory < DinnaerFactory
  def createMain
    Rice.new
  end
  def createAppetizer
    Soup.new
  end
end

class ItalyFactory < DinnaerFactory
  def createMain
    Pizza.new
  end
  def createAppetizer
    Salad.new
  end
end

japanese = JapaneseFactory.new
puts japanese.createDinner

italy = ItalyFactory.new
puts italy.createDinner
```

## Factory Method
### 概要
インスタンスの生成をサブクラスに任せる
インスタンスの生成部分を切り離すことで、結合度を下げて追加・変更・保守を容易に

### 例

```rb
class Saxophone
  def initialize(name)
    @name = name
  end
  def play
    puts "#{self.class} #{@name} is being played!"
  end
end

class Trumpet
  def initialize(name)
    @name = name
  end
  def play
    puts "#{self.class} #{@name} is being played!"
  end
end

class InstrumentFactory
  def initialize(number_instruments)
    @instruments = []
    number_instruments.times do |i|
      instrument = new_instrument("No.#{i + 1}")
      @instruments << instrument
    end
  end
  def ship_out
    @tmp = @instruments.dup
    @instruments = []
    @tmp
  end
end

class SaxophoneFactory < InstrumentFactory
  def new_instrument(name)
    Saxophone.new(name)
  end
end

class TrumpetFactory < InstrumentFactory
  def new_instrument(name)
    Trumpet.new(name)
  end
end

factory = SaxophoneFactory.new(3)
saxophones = factory.ship_out
saxophones.each { |saxophone| saxophone.play }

factory = TrumpetFactory.new(2)
trumpets = factory.ship_out
trumpets.each { |trumpet| trumpet.play }
```