Factory Method を Ruby で
Factory Method は、 生成に関するデザインパターンの一つで、 具象クラスを指定することなく、 プロダクト (訳注: 本パターンでは、 生成されるモノのことを一般にプロダクトと呼びます) のオブジェクトを生成することを可能とします。
Factory Method では、 オブジェクトの生成において、 直接のコンストラクター呼び出し (new 演算子) 代わりに使用すべきメソッドを定義します。 サブクラスにおいてこのメソッドを上書きすることにより、 生成されるオブジェクトのクラスを変更します。
もし各種ファクトリー系のパターンやコンセプトの違いで迷った場合は、 ファクトリーの比較 をご覧ください。
複雑度:
人気度:
使用例: Factory Method パターンは、 Ruby コードでは広く使われます。 コードに高度の柔軟性を持たせたい時にとても役に立ちます。
見つけ方: 具象クラスで具象オブジェクトを作成し、 それを抽象型またはインターフェースのオブジェクトとして返すような生成メソッドの存在により、 Factory Method を識別できます。
概念的な例
この例は、 Factory Method デザインパターンの構造を説明するためのものです。 以下の質問に答えることを目的としています:
- どういうクラスからできているか?
- それぞれのクラスの役割は?
- パターンの要素同士はどう関係しているのか?
main.rb: 概念的な例
# The Creator class declares the factory method that is supposed to return an # object of a Product class. The Creator's subclasses usually provide the # implementation of this method. class Creator # Note that the Creator may also provide some default implementation of the # factory method. def factory_method raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end # Also note that, despite its name, the Creator's primary responsibility is # not creating products. Usually, it contains some core business logic that # relies on Product objects, returned by the factory method. Subclasses can # indirectly change that business logic by overriding the factory method and # returning a different type of product from it. def some_operation # Call the factory method to create a Product object. product = factory_method # Now, use the product. "Creator: The same creator's code has just worked with #{product.operation}" end end # Concrete Creators override the factory method in order to change the resulting # product's type. class ConcreteCreator1 < Creator # Note that the signature of the method still uses the abstract product type, # even though the concrete product is actually returned from the method. This # way the Creator can stay independent of concrete product classes. def factory_method ConcreteProduct1.new end end class ConcreteCreator2 < Creator # @return [ConcreteProduct2] def factory_method ConcreteProduct2.new end end # The Product interface declares the operations that all concrete products must # implement. class Product # return [String] def operation raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end # Concrete Products provide various implementations of the Product interface. class ConcreteProduct1 < Product # @return [String] def operation '{Result of the ConcreteProduct1}' end end class ConcreteProduct2 < Product # @return [String] def operation '{Result of the ConcreteProduct2}' end end # The client code works with an instance of a concrete creator, albeit through # its base interface. As long as the client keeps working with the creator via # the base interface, you can pass it any creator's subclass. def client_code(creator) print "Client: I'm not aware of the creator's class, but it still works.\n"\ "#{creator.some_operation}" end puts 'App: Launched with the ConcreteCreator1.' client_code(ConcreteCreator1.new) puts "\n\n" puts 'App: Launched with the ConcreteCreator2.' client_code(ConcreteCreator2.new) output.txt: 実行結果
App: Launched with the ConcreteCreator1. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct1} App: Launched with the ConcreteCreator2. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}