🔖
判定ロジックはPolicyパターンに集約
Daily Blogging96日目
判定ロジックがcontrollerにあるのが邪魔くさい
似通ったロジックも点在してる
そうだPolicyパターンを使おう
改訂新版 良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方
この本でも紹介されているPolicyパターン
これを使えば判定ロジックをスッキリさせることができそう
Policyパターンとは
本によると
判定条件(ルール)を部品化し、部品化した条件の組み合わせを可能にするしくみ
ルール :部品
ポリシー:組み合わせ
みたいな感じ
サンプルコードを作ってみる
異なるコンテンツタイプに対して、それぞれの購入可能判定ロジックが必要な場合
例えばこうできる?
ベースとなるPolicy
# コンテンツタイプに関わらず共通する処理
class BasePurchasePolicy
def initialize(user, content)
@user = user
@content = content
@rules = []
add_common_rules
setup_rules # サブクラスで固有ルールを追加
end
def add(rule)
@rules << rule
end
def comply_with_all
@rules.all?(&:ok)
end
private
def add_common_rules
add(UserActiveRule.new(@user))
add(EnoughBalanceRule.new(@user, @content))
end
def setup_rules
# サブクラスでオーバーライド
end
end
ルールごとのクラス
class UserActiveRule
def self.ok(user)
user.active?
end
end
class EnoughBalanceRule
def self.ok(user, content)
user.balance >= content.price
end
end
class EbookLicenseRule
def self.ok(user, ebook)
!user.ebooks.include?(ebook) # すでに所有していないかを確認
end
end
コンテンツごとのPolicy
# EBook用
class EbookPurchasePolicy < BasePurchasePolicy
private
def setup_rules
add(EbookLicenseRule.new(@user, @content)) # 電子書籍特有のルール
end
end
# Video用
class VideoPurchasePolicy < BasePurchasePolicy
private
def setup_rules
add(RegionRestrictionRule.new(@user, @content)) # 動画特有のルール
end
end
EBookを買う時はこういうコントローラにする
class EbooksController < ApplicationController
# 必要なロジックは1つのbefore_actionに集約される!
before_action :authorize_purchase, only: [:purchase]
def purchase
user = User.find(params[:user_id])
ebook = Ebook.find(params[:id])
# 購入処理
user.update(balance: user.balance - ebook.price)
render json: { message: '電子書籍の購入成功' }, status: :ok
end
private
def authorize_purchase
user = User.find(params[:user_id])
ebook = Ebook.find(params[:id])
policy = EbookPurchasePolicy.new(user, ebook)
unless policy.comply_with_all
render json: { message: '購入不可' }, status: :forbidden
end
end
end
メリット
それっぽいコードを作ってみた感想
- controllerのメインロジックに集中できる
- before_actionがスッキリする
- controllerのテスト項目を減らせそう
- 責務が分離されてる
- ルールが変更になった時は、それぞれのPolicyクラスを修正するだけですむ
Discussion