LoginSignup
6

More than 5 years have passed since last update.

Null Object パターンを使用して Model のデフォルト値設定を集約する

Posted at

以下の 2 つの Model があるとします。

app/models/user.rb
class User < ActiveRecord::Base
  has_one :setting, dependent: :destroy
end
app/models/setting.rb
class Setting < ActiveRecord::Base
  belongs_to :user

  enum format: %i(plain markdown textile)
  enum notification: %i(none email push)
end

User は 0 件あるいは 1 件の Setting を持ちます。

user = User.find_by(name: '佐倉 杏子') #=> #<User>
user.setting #=> #<Setting>
user.setting.format #=> "markdown"
user.setting.notification #=> "email"

user = User.find_by(name: '美樹 さやか') #=> #<User>
user.setting #=> nil

ここで Setting の値を使用しているコードのことを考えてみます。

case user.setting.format
when 'plain'
  # プレーンテキストを処理
when 'markdown'
  # markdown 記法を処理
when 'textile'
  # textile 記法を処理
end

上記の case 文では user.setting が nil のケースを考慮できていません。そのため以下の様に書き換えます。

case user.setting&.format || 'plain'
when 'plain'
  # プレーンテキストを処理
when 'markdown'
  # markdown 記法を処理
when 'textile'
  # textile 記法を処理
end

Setting が存在しない場合、デフォルト値として 'plain' を指定するようにしました。

しかし、このアプローチでは user.setting を参照しているすべてのコードで同じような記述をしなければならないでしょう。DRY ではないし、そもそも呼び出し元で設定があるかないかを逐一調べたくはありません。 :disappointed_relieved:

ここで便利なのが、デザインパターンの Null Object パターン です。

空の Setting を表すクラスを定義し、そこにデフォルト値の設定を集約するのです。

app/models/setting/null.rb
class Setting
  class Null
    def format
      'plain'.freeze
    end

    def notification
      'none'.freeze
    end
  end
end

NullSetting というクラス名にしてもよかったんですが、そうすると app/models 直下がごちゃごちゃしそうなので Setting::Null というクラス名にしてみました。

そして User に setting_or_missing というメソッドを定義します。

app/models/user.rb
class User < ActiveRecord::Base
  has_one :setting, dependent: :destroy

  def setting_or_missing
    setting || Setting::Null.new
  end
end

この setting_or_missing メソッドを使用すれば、呼び出し元は User が Setting を持っているか否かに関心を持たずに済みます。:blush:

case user.setting_or_missing.format
when 'plain'
  # プレーンテキストを処理
when 'markdown'
  # markdown 記法を処理
when 'textile'
  # textile 記法を処理
end

参考

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
6