本題
- Railsで書いてるとクラスメソッドでいろいろやりがちにならない?と思ったので解決策を考える。
ファーストクラスコレクション
良いコード、悪いコードで学ぶ設計入門という本を最近読んで、ファーストクラスコレクションという物があることを知りました。
今までrailsのみで開発をしてきたので、こんな書き方があるんだなーと思いました。
それと同時に Railsのクラスメソッドでいろいろやりすぎ?? という疑問が浮かびました。
原因
ActiveRecord::Relation
がクラスメソッドを実行できるのでそうなりがちなのでは?と思いました。
User modelが存在すると仮定して、APIでユーザーの名前、年齢を返すAPIを想定します。
# app/models/user.rb
class User < ActiveRecord::Base
scope: otona, -> { where('age >= 18') }
def self.name_and_age
all.each_with_object([]) { |user, result|
result << { name: user.name, age: user.age }
}
end
end
# app/controllers/api/users_controller.rb
class UsersController < ApplicationController
def index
render json: { users: User.otona.name_and_age }
end
end
ここで違和感が発生します。
User#name_and_age
がUserクラスに存在する違和感、つまり、これってUsersクラスで良くない?ってことです。
Userクラスはあくまでそのクラスのインスタンスである1ユーザーのことに集中すべきで、そうすることで責務も適切に分けることができそうな気がします。
以下のように変更してみます。
# app/models/users.rb
class Users
def initialize(users)
@users = users
end
def name_and_age
@users.each_with_object([]) { |user, result|
result << { name: user.name, age: user.age }
}
end
end
# app/controllers/api/users_controller.rb
class Userscontroller < ApplicationController
def index
render json: { users: Users.new(User.otona).name_and_age }
end
end
パッと見た感じ良い感じに責務を分けれたように思えます。
ただ、こんな単純な場合ではなく、Usersクラスの中で @users.includes(....)
などすることは考えられるため、ActiveRecord::Relationをnewする時に渡す、という使い方になりそうな気がします。そうなると、ただクラスメソッドを別のクラスに移しただけでは?というような気もしてきますよね。
最後に
Railsで開発しているとオブジェクト指向を意識する機会が少ないのかもしれないと思ってきました。有識者の皆さんの意見をお待ちしてます!