Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。
ざっくり言うと?
インスタンスの生成部分を、サブクラスにお任せするパターン
使い道
Template Method パターン のインスタンスを使って、何かしらの処理を行う class をキレイに実装する
実装例
StringLister#display の内容を画面に出力する StringListerViewer を作る
factory_method.rb
# Creator に相当
class StringListerViewer
attr_reader :items
def initialize(items)
@items = items
end
def display # anOpreation に相当
puts string_lister(items).display
end
def string_lister(items) # factoryMethod に相当
raise NotImplementedError
end
end
# ConcreteCreator に相当
class TextStringListerViewer < StringListerViewer
def string_lister(items)
# ConcreteProduct に相当
TextStringLister.new(items)
end
end
# ConcreteCreator に相当
class HtmlStringListerViewer < StringListerViewer
def string_lister(items)
# ConcreteProduct に相当
HtmlStringLister.new(items)
end
end
items = %w[abc def ghi]
HtmlStringListerViewer.new(items).display
# <html><body>
# <div>abc</div>
# <div>def</div>
# <div>ghi</div>
# </body></html>
TextStringListerViewer.new(items).display
# abc
# def
# ghi
- StringListerViewer クラス → anOparation を実装
- StringListerViewer のサブクラス → factoryMethod を実装
- インスタンスの生成を、各サブクラスに投げる
- StringListerViewer の結合度が下がる
- StringListerViewer のサブクラスでは、インスタンスの生成を実装するだけで済むようになる
ConcreteCreator を作らないで、動的に Product のサブクラスを生成してみる
Object.const_get を使って、文字列からクラスを取得する
大量に Product のサブクラスがある時に、いちいち ConcreteCreator クラスを作らないので楽
factory_method.rb
class StringListerViewer
attr_reader :items, :type
def initialize(items, type)
@items = items
@type = type
end
def string_lister(items)
Object.const_get("#{type.to_s.capitalize}StringLister").new(items)
end
def display
puts string_lister(items).display
end
end
items = %w[abc def gih]
StringListerViewer2.new(items, :text).display
StringListerViewer2.new(items, :html).display
Factory パターンのその他の利点
コンストラクタがシンプルになる
- 複雑な処理はインスタンス生成後に、別メソッドとして呼び出す事ができる
- 複雑な事前処理を分離することで、実装がシンプルになったりテストが書きやすくなる
- 事前処理が別メソッドでも、生成はファクトリメソッドに一任されているから、事前処理の呼び出しもれが無い
参考
→ Abstruct Factorty パターン に続く