概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Refactor your Ruby on Rails application with policy object pattern
- 原文公開日: 2018/01/29
- 著者: Paweł Dąbrowsk
Rails tips: Policy Objectパターンでリファクタリング(翻訳)
Policy Objectは操作の分離に用いられる素朴なRubyオブジェクトのことです。個人的にこのパターンが好きですが、オブジェクトにPolicy Objectとしてふさわしい名前をつけるために従っておくべきルールがいくつかあります。
Policy Objectの規則
- メソッド名の末尾は常に
?
にする - メソッドは
true
かfalse
のいずれかだけを返す - 渡された属性は変更しない
- コードはシンプルな読み出しロジックだけを行う: データベース呼び出しなどは行わない
デモ
サンプルのクラスを作ってみましょう。後ほどPolicy Objectをここに実装します。
class UserService
def initialize(user)
@user = user
end
def name
if user.full_name.blank? && user.email.present?
user.email
else
user.full_name
end
end
def account_name
if user.sign_in_count > 0 && user.role == "admin"
"Administrator"
else
"User"
end
end
private
attr_reader :user
end
このクラスは「読み取り」と「チェック」という2つの操作に簡単に分けられます。チェック部分はそのままPolicy Objectとして完璧なコードです。User
オブジェクトに対して操作を行っているので、2つのメソッドのためのPolicy Objectを1つ作成できます。
class UserPolicy
def initialize(user)
@user = user
end
def administrator_account_name?
user.sign_in_count > 0 && user.role == "admin"
end
def use_email_as_name?
user.full_name.blank? && user.email.present?
end
private
attr_reader :user
end
UserService
サービス(Service Object)にUserPolicy
を実装すると、コードがとてもスッキリしました。
class UserService
def initialize(user)
@user = user
end
def name
user_policy.use_email_as_name? ? user.email : user.full_name
end
def account_name
user_policy.administrator_account_name? ? "Administrator" : "User"
end
private
attr_reader :user
def user_policy
@_user_policy ||= UserPolicy.new(user)
end
end
読み取り部分からポリシーのロジックが分離され、UserPolicy
をシンプルな方法でスタブできるようになったのでUserService
クラスがテストしやすくなりました。Rails tips: ロジックをわかりやすい変数に置き換えるリファクタリング(翻訳)【要リンク変更】をこのPolicy Objectに適用することもできます。ロジックを変数に移す代わりに、意味のある名前を持つもっと小さなメソッドにロジックを直接移してみましょう。
class UserPolicy
def initialize(user)
@user = user
end
def administrator_account_name?
user_signed_in? && user_is_administrator?
end
def use_email_as_name?
user_does_not_have_full_name? && user_has_email?
end
private
attr_reader :user
def user_does_not_have_full_name?
user.full_name.blank?
end
def user_has_email?
user.email.present?
end
def user_signed_in?
user.sign_in_count > 0
end
def user_is_administrator?
user.role == "admin"
end
end
クラスのコード量は増えたものの、コードが自ら語るようになりました。
Railsでお困りの方にお知らせ
知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。
RSpec & TDDの電子書籍を無料でダウンロード
もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『RSpec & Test Driven Developmentの無料ebook』をどうぞお役立てください。