LoginSignup
11
9

More than 5 years have passed since last update.

Railsアンチパターン:IDを引数にする

Last updated at Posted at 2018-12-28

Railsアプリを扱っていると、↓のような「オブジェクト1のIDを引数にするメソッド」をたまに見かけます。

class BlogEntry < ApplicationRecord
  belongs_to :user

  # ブログ記事を作成する。user_id は User の id
  def self.create_entry(title:, body:, user_id:)
    h = { title: title, body: body, user_id: user_id }

    # 内容をチェックしたり、値を追加したりするなどの、前処理をする

    self.create!(h) # エントリ作成
  end 
end

しかし、私はこれはアンチパターンであり、idではなくオブジェクトそのものを渡す方が良いと思います。

  def self.create_entry(title, body, user_id)
    
  def self.create_entry(title, body, user)

IDを引数にすることの問題点

オブジェクトではなくidを渡しているのは、

  • オブジェクトを引数にすると、メソッド呼び出しが遅くなる
  • オブジェクトを引数にすると、呼び出し元でオブジェクトを取得する処理(User.find(params[:user_id])とか)をしなければならなくなり、SQLを実行する回数が1回増えてしまう

といった考えからだと思いますが、どれも誤りです。

オブジェクトを引数にしてもメソッド呼び出しは遅くなりません。C++やGoでは、構造体を引数にすると値のコピーが発生して速度が落ちることがあります。しかし、Rubyでは引数は参照渡しされるので、オブジェクトを引数にしても、整数(id)を引数にしたときと速度は変わりません2

idを引数にしても、SQL呼び出し回数は減りません。idを引数に取ようなメソッドでは、

  • id に対応するオブジェクトが存在するかのチェック
  • オブジェクトが、前提条件を満たしているかのチェック

などもしなければならないはずです(たとえ今は不要でも後から必要になることは少なくありません)。すると、結局 User.find などを呼び出すことになります。

現実には、後付けで機能追加していった結果、コントローラーとモデルの両方で、オブジェクトの存在確認をしているので、むしろ呼び出し回数が増えているケースすら見たことがあります。

# 後付けで修正した結果、無駄なSQL呼び出しをしているケース

# Controller側のコード
unless User.exists?(id: params[:user_id])
  render :error, alert: "ユーザーが見つかりません"
  return
end
BlogEntry.create_entry(title: title, body: body, user_id: params[:user_id])

# BlogEntry
def self.create_entry(title:, body:, user_id:)
  user = User.find_by(id: user_id)
  raise "ユーザーが見つかりません" if user.nil?
  raise "ユーザーが凍結中です" if user.frozen?

  # 実際の処理
end

IDではなくオブジェクトを引数にするべき

こういうわけで、Railsでメソッドを設けるときは、user_id とか entry_id みたいなIDではなく、
user, entryのようなオブジェクトを引数にするようにしてください。

上述のように、オブジェクトを引数にしてもC++やGoのように遅くなることはありませんし、.find を二重に呼び出す必要もなくなるのでかえって速くなるはずです。


  1. この記事では 「ActiveRecord::Base のサブクラスのインスタンス」を「オブジェクト」と呼んでいます。 

  2. 実際には整数型の方が、内部表現が異なるとか、JITで最適化される余地があるとかで、速くなるかもしれません。ただ、大抵のRailsアプリでは、整数型にすることによる高速化より、オブジェクト型にするメリットの方が大きいはずです。 

11
9
0

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
11
9