woshidan's loose leaf

ぼんやり勉強しています

Factory Methodパターン

Template Methodパターンをインスタンスの生成の場面に適用したものがFactory Methodパターンだそうです。

具体的なもので言うと、ActiveRecordとか、それっぽいというか、 ActiveRecordのコードをちょっと読んだときにfactoryという言葉を見かけて以来、 取り上げようと思っていたというか。

FactoryMethodパターンは、

  • 特定の性質を持つメソッド等を持つクラス(Product)
    • それを作るメソッドを持つクラス(Creator)と、
  • その具体的なメソッドの内容を持つ(ConcreteProduct)クラス
    • それを作るメソッドを持つクラス(ConcreteCreator)

が登場します。

Rubyでそれっぽいの無いかな、と思っていたら、ActiveRecordがそれっぽい気がします。

ActiveRecordのFactory Methodパターンに当てはめて考えてみると以下のような感じ。

  • ActiveRecord::Baseのサブクラスはクラス名と対応するDB上のテーブルとやり取りするためのクラス
  • ActiveRecord::Baseには、インスタンスの作成方法や、データベースとの接続方法等や関連するテーブルとやり取りを定義するためのメソッドは定義されているが、具体的なテーブルや、その定義のメソッドはサブクラスで呼び出す
    • Creatorっぽい。

これとは別個に、ActiveRecordのfactory-typeメソッドというのを持っています。

これは、関連するActiveRecordのクラスとの間に、指定した名前を持つ、特定の操作方法を持ったメソッドを与えます。

Userクラス内でhas_many :itemsと書いたら、Userクラスのインスタンスメソッドに、関連するItemクラスのインスタンスを取得できる#itemsメソッドが追加される、という感じ。

最初は、ActiveRecordそのものが、Factory Methodパターンっぽいと思っていたのですが、 ここまで書いて、それ自体は普通にクラスとインスタンスの話で、関連する特定の性質をもったインスタンスが生成されるメソッドを使う、という意味では、 このfactory-typeメソッドがまさにFactory Methodパターンなのかも。

...has_manyメソッドを持っているActiveRecord::Baseの段階ではどのクラスがどのクラスと一対多で対応するようなクエリを書くメソッドになっているかを決めていない状態になっていて、サブクラスでhas_manyメソッドを呼び出して、具体的な引数を与える事で、決まったテーブルとの間で一対多のクエリを作るメソッドを追加する、みたいな......。

factory-typeメソッドについてはこの辺。

https://github.com/rails/rails/blob/a19200d7bde3a852bad9efd3e5430aea3f788e1c/activerecord/lib/active_record/associations.rb

難しくなってきたんですが、決まった対応関係を持ったクラスの組を複数実装するとき、対応する組の間ではどのような処理が行われるか決まっているけど、その組は実装するまで分からない...という場合に使える、という感じですかね。