マイペースなRailsおじさん

Ruby、Ruby on Rails、オブジェクト指向設計を主なテーマとして扱います。だんだん大きくなっていくRuby on Rails製プロダクトのメンテナンス性を損なわない方法を考えたり考えなかったりしている人のブログです。

Active RecordとNull Objectパターン

Active RecordでNull Objectパターンを使うことはできるのか考えてみました。

RubyでNull Objectパターン

RubyでもNull Objectパターンを使えます。下記の記事を御覧ください。

ytnk531.hatenablog.com

Active RecordとNull Objectパターン

どんなときに使いたいか

アソシエーションを張っているレコードがない場合の処理を書きたいときに、Null Objectパターンを使えたら便利そうです。

実装

class Customer < ApllicationRecord
  has_one :actual_license, class_name: 'License'

  def license
    actual_license || NullLicense.new
  end
end

class License < ApplicationRecord
  belongs_to :customer
end

class NullLicense
end

このようにすれば、アソシエーションしているLicenseレコードが無い場合はNullLILicenseを渡すことができます。実際のlicenseとの関連はCustomer#actual_licenseを呼び出すことになります。

実装のポイント - has_oneのclass_nameオプションを使ってLicenseを取得するメソッドの名前を変更する - licenseメソッドのゲッタを作ることでNullLicenseのインスタンスを返せるようにする

困りそうなポイント

NullLicenseのほうに値の無い処理が書けるようになりましたが、その代わり、構造上のややこしさを生み出しました。

  • ライセンスのない場合の処理を外部に書きたいとき、Customer#license.nil?のような判定をすると意図していない結果になる(NullLicenseが取得できるため)
  • licenseかlicense_actualかを呼び出し側で考慮しないと行けない部分が出てくるかもしれない

といったところです。2点目の回避策としては、NullLicenseの方にLicenseインスタンスが外から呼び出されるメソッドを実装してしまうという方法があります。例えばLicense#destroyが呼ばれる場合、このようにしてダミーのメソッドを作ります。

class NullLicense
  def destroy
  # 何もしない
  end
end

この方法は、NullLicenseの方に値がないときの処理がまとまる利点がありますが、構造の把握をややこしくしてしまうという欠点もあります。

Active RecordでNull Objectパターンはややこしいコードを生んでしまうかも

Null Objectパターンを使うためにアソシエーションの張り方を歪めているので、新たな問題を生んでしまう可能性があります。

このあたりのデメリットを許容した上で、値がないときのややこしい処理がビューやコントローラーに散ってしまうことの対策として使うのがよさそうです。