YOSHINO日記

プログラミングに関すること

Railsでデザインパターン「Lazy Factory」

Lazy Factory??

遅延読み込み - Wikipedia

デザインパターンの観点では遅延初期化はFactory Method パターンと共に用いられる。これは3つのアイデアを組み合わせである。

  1. クラスインスタンスを得るためにファクトリメソッドを利用する(Factory Method パターン)
  2. インスタンス連想配列に保存し、これにより次回から同じパラメータで「同じ」インスタンスを得られる(Multiton パターン)
  3. 初めてオブジェクトが必要とされた時点でインスタンス化を行う(遅延初期化パターン)

FactoryMethodに関して

デザインパターン「Factory Method」 - Qiita

インスタンスの作り方をスーパークラスで定め、具体的な処理をサブクラスで行うパターンをFactory Methodパターンと呼びます。 オブジェクトの生成と具体的な処理を分離することで、より柔軟にオブジェクトを利用することができます。 Template Methodパターンをオブジェクト生成の場面に適応させたデザインパターンと言えますね。 簡単に言えば、オブジェクト生成を容易にするデザインパターンです。

RailsでDHHの具体的な実装を見てみます。

Use lazy-load factory method for site configuration

上記のタイトルは、ActiveStorageのDHHによるコミットメッセージです。

Use lazy-loaded factory method for site configuration · rails/activestorage@efd950a · GitHub

このコミットでDHHはデザインパターンの1つである「Lazy Factory」を利用してリファクタリングを行っています。

まず、リファクタリングをする前から見ていきます。

以下のようなクラスが定義されています。

スーパークラス

  • lib/active_file/site.rb

サブクラス:

  • lib/active_file/site/disk_site.rb
  • lib/active_file/site/gcs_site.rb
  • lib/active_file/site/mirror_site.rb
  • lib/active_file/site/s3_site.rb

初期化はサブクラスから直接行われるので、例えば以下のようにインスタンスを生成します。

SITE = ActiveFile::Sites::DiskSite.new(root: File.join(Dir.tmpdir, "active_file"))

次にリファクタリング後を見てみます。

まず、スーパークラスが定義されているlib/active_file/site.rbを見てみます。

class ActiveFile::Site
  def self.configure(site, **options)
    begin
      require "active_file/site/#{site.to_s.downcase}_site"
      ActiveFile::Site.const_get(:"#{site}Site").new(**options)
    end
  end

...

configureメソッドを作成し、サブクラスのタイプとオプションを受け取って、サブクラスを生成しています。

この結果、サブクラスの生成は直接サブクラスから生成するのではなく、スーパークラスを経由して生成するようになります。

SITE = ActiveFile::Site.configure(:Disk, root: File.join(Dir.tmpdir, "active_file"))