Tech Racho エンジニアの「?」を「!」に。
  • 開発

Rails tips: Null Objectパターンでリファクタリング(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Rails tips: Null Objectパターンでリファクタリング(翻訳)

Null Objectパターンによるリファクタリングは、指定されたオブジェクトが存在するかどうかをチェックして、存在しなかった場合に指定の属性やメソッドのデフォルト値を返す操作に適用できます。このような操作ではif条件が必要になることが多く、そのままではコードが少々読みづらいうえにテストも少しばかりやりにくくなります。Null Objectパターンを使うことでコードが非常にシンプルになり、テストも簡単になります。

Null Objectパターンを使うメリットをわかりやすく示すため、次のような事例を考えてみましょう。UserPostという2つのクラスがあり、UserクラスのオブジェクトはPostクラス上で操作を行います。

class User < ActiveRecord::Base
  has_many :posts

  def latest_post_title
    post = posts.order('created_at DESC').first

    if post.present?
      post.title
    else
      "No posts yet"
    end
  end
end

「単一責任の原則」からほど遠いコードです。ここでは以下の操作を行っています。

  1. 最新のpostをフェッチする
  2. postが存在するかどうかをチェックする
  3. postが存在する場合はposttitleを表示する
  4. postが存在しない場合は適切な情報を表示する

こんなときはNull Objectパターンの出番です。まずは新しいオブジェクトを作成しましょう。

class NoPost
  def title
    "No posts yet"
  end
end

シンプルなロジックを備えた、ごくシンプルなRubyオブジェクトができました。それではUserモデルで以下を行ってリファクタリングしましょう。

  1. クエリを別のメソッドに切り出す
  2. NoPost Null Objectを用いて、最新のpostの代入を別のメソッドに切り出す
  3. メソッドの責務を「最新のposttitleを返す」シンプルな責務に変える

Userクラスにこれらを実装すると、以下のように明快かつ読みやすいクラスに変わりました。

class User < ActiveRecord::Base
  has_many :posts

  def latest_post_title
    lastest_post.title
  end

  private

  def latest_post
    find_latest_post || NoPost.new
  end

  def find_latest_post
    posts.order('created_at DESC').first
  end
end

User#latest_post_titleの内容が明快になり、if条件も消滅しました。もうひとつ重要な点は、このNull Objectに適切な名前をつけて、何がしたいのかが名前からわかるようにすることです。


Railsでお困りの方にお知らせ

知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。

Railsパターンの電子書籍を無料でダウンロード

もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『Introduction Rails patterns』をどうぞお役立てください。

関連記事

Railsの`Object#try`がダメな理由と効果的な代替手段(翻訳)

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。