5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaエンジニアがRubyでデザインパターンを学ぶ - Factory Pattern

Posted at

概要

「Rubyによるデザインパターン」を読んでデザインパターンを勉強中。
Javaをやっていた人間としての目線で情報を整理してみます。

今までに整理したもの

Template Method Pattern
Strategy Pattern
Observer Pattern
Composite Pattern
Command Pattern
Proxy Pattern
Decorator Pattern

Factory pattern

  • 「オブジェクトの生成」をカプセル化する、というアイデア
  • GoF のデザインパターンでは「Factory Method」と「Abstract Factory」の2つ

これまでなんとなくモヤッとしていたんですが「Rubyによるデザインパターン」にとてもシンプルで分かりやすい解説があったので引用します。

In the same way that the Factory Method pattern is really the Template Method pattern applied to object creation, so the Abstract Factory pattern is simply the Strategy pattern applied to the same problem.

オブジェクトの生成に Template Method を適用したものが Factory Method, Strategy を適用したものが Abstract Factory だ、ということです。詳細は以下で。

Factory Method pattern

オブジェクト生成を子クラスに移譲する。

###実装例

MisoSoup
class MisoSoup
  attr_reader :name

  def prepare
    puts "Preparing"
  end

  def make_dashi
    puts "Make dashi"
  end

  def boil
    puts "Boil"
  end

  def add_miso
    puts "Add miso"
  end
end

class KantoStyleMisoSoup < MisoSoup
  def initialize
    @name = "Kanto Style"
  end
end

class KansaiStyleMisoSoup < MisoSoup
  def initialize
    @name = "Kansai Style"
  end
end
JapaneseRestaurant
class JapaneseRestaurant
  # オブジェクトの生成部分を子クラスに移譲した template method とする
  def order_miso_soup
    # soup = KantoStyleMisoSoup.new  とせずに抽象化する
    soup = new_miso_soup

    soup.prepare  # 下拵え
    soup.boil     # 具材を入れつつ煮立たせる
    soup.add_miso # みそ投入

    soup
  end

  def new_miso_soup
    raise "Must be override!"
  end
end


class KantoStyleRestaurant < JapaneseRestaurant
  # このように具体的なオブジェクトを生成することを目的としたメソッドを factory method と言う
  def new_miso_soup
    KantoStyleMisoSoup.new
  end
end

class KansaiStyleRestaurant < JapaneseRestaurant
  # 子クラスで factory method を override
  def new_miso_soup
    KansaiStyleMisoSoup.new
  end
end
main
restaurant = KantoStyleRestaurant.new
miso_soup = restaurant.order_miso_soup

puts "Got a cup of #{miso_soup.name} miso soup."

Abstract Factory pattern

オブジェクトの生成を別のオブジェクトに移譲。
特定のオブジェクトの組み合わせを生成することを目的としたクラス(Abstract Factory)を定義。

###実装例

MisoSoup
class MisoSoup
  attr_reader :dashi, :miso, :ingrediants

  def initialize(factory)
    @factory = factory
  end

  def prepare
    # ingrediants, dashi, miso オブジェクトの生成は factory オブジェクトに移譲
    # この部分が Strategy pattern となっている
    @ingrediants = @factory.createIngrediants
    @dashi = @factory.createDashi
    @miso = @factory.createMiso

    puts "Preparing #{@ingrediants.join(', ')}."
  end

  def make_dashi
    puts "Make dashi from #{@dashi}"
  end

  def boil
    puts "Boil #{@ingrediants.join(', ')}."
  end

  def add_miso
    puts "Add #{@miso} into the pot."
  end
end


class KantoStyleMisoSoupFactory
  # Abstract Factory の実装として各オブジェクトの生成メソッドは factory method である場合も多い
  def createMiso
    AkaMiso.new
  end

  def createDashi
    Katsuo.new
  end

  def createIngrediants
    [Tofu.new, Wakame.new, Negi.new]
  end
end

class KansaiStyleMisoSoupFactory
  def createMiso
    ShiroMiso.new
  end

  def createDashi
    Kombu.new
  end

  def createIngrediants
    [Tofu.new, Wakame.new, Negi.new]
  end
end
JapaneseRestaurant
class JapaneseRestaurant
  def initialize(factory)
    @factory = factory
  end

  def order_miso_soup
    soup = MisoSoup.new(@factory)

    soup.prepare  # 下拵え
    soup.boil     # 具材を入れつつ煮立たせる
    soup.add_miso # みそ投入

    soup
  end
end
main
restaurant = JapaneseRestaurant.new(KantoStyleMisoSoupFactory.new)
miso_soup = restaurant.order_miso_soup

###より Ruby らしく

Ruby においてクラスは Class クラスのインスタンスであるので、クラス自体をコンストラクタの引数で渡すようにすればより抽象化された Factory クラスにできる。

MisoSoupFactory
class MisoSoupFactory
  def initialize(miso_class, dashi_class, *ingrediant_classes)
    @miso_class = miso_class
    @dashi_class = dashi_class
    @ingrediant_classes = ingrediant_classes
  end

  # Ruby でのインスタンス生成はあくまでもそのクラスオブジェクトの new メソッドを呼び出しているだけなので
  # 次のような形でシンプルに記述できる
  def createMiso
    @miso_class.new  
  end

  def createDashi
    @dashi_class.new
  end

  def createIngrediants
    list = []
    @ingrediant_classes.each do |ingrediant_class|
      list << ingrediant_class.new
    end
    list
  end
end

# Factory クラスのコンストラクタに必要なクラスオブジェクトを渡す
factory = MisoSoupFactory.new(AkaMiso, Katsuo, Tofu, Wakame, Negi)
restaurant = JapaneseRestaurant.new(factory)
miso_soup = restaurant.order_miso_soup

####Javaの場合
次のようにすればインスタンス生成時にクラスを動的に変更できますが、型情報を抽象化するのはある意味で Java の利点を損なうことになるので多用すべきではない、と個人的には思います。

String name = "AkaMiso";
Class.forName(className).newInstance();
// または
Class clazz = AkaMiso.class;
clazz.newInstance();

参考

Olsen, R. 2007. Design Patterns in Ruby

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?