はじめに
こんにちは。アメリカ在住で独学でエンジニアを目指している taira です。
現在時差ぼけに苦しみながらも、少しずつですが勉強のリズムを取り戻しつつあり、本日からRailsチュートリアルの勉強を再開しました
そこで出てきたクラスメソッドとインスタンスメソッドの使いわけについて、まだ理解しきれていないところがあったので再度確認のために記事を書いております
コード内容
今回は具体的なコードを用いて説明を進めていきます。
以下のコードはRailsチュートリアルのコードを一部拝借させていただいております
class User < ApplicationRecord
・
・
・
# 渡された文字列のハッシュ値を返す
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost:cost)
end
# ランダムなトークンを返す
def User.new_token
SecureRandom.urlsafe_base64
end
# 永続セッションのためにユーザーをデータベースに記憶する
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
end
ここで、渡された文字列のハッシュ値を返すUser.digest(string)
メソッドと、ランダムなトークンを返すUser.new_token
はクラスメソッドとして扱われています。
一方、永続セッションのためにユーザーをデータベースに記憶するremember
メソッドはインスタンスメソッドです。
上記を見て「User.digest
もUser.new_token
も、ユーザーごとにあることを踏まえるとインスタンスメソッドとして扱うのがもっとものように思えるのですが、なぜクラスメソッドとして定義しているのだろうと思いました」
それぞれの違い
これの解答としては、User.digest
もUser.new_token
も「特定のユーザー自身とは関係がなく、ユーザーという概念一般に対して共通の処理であるため」クラスメソッドに定義しています。
ポイントは、これらのメソッドが「個別のユーザーインスタンスに紐付く情報を一切使っていない」点にあります。
それぞれのメソッドごとに見ていきましょう
1. User.digestの場合
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
このメソッドは、受け取った文字列を単にハッシュ化するだけであり、
- どのユーザーの情報も必要としていません。
- 個々のユーザーごとの状態に依存しません。
つまり、ユーザーAさんの情報があってもなくても、このメソッドは完全に独立して動作します。
ユーザーオブジェクトのインスタンス変数に一切アクセスせず、単純な「汎用のハッシュ生成機能」を提供しています。
そのため、「特定のユーザーのための処理」ではなく、「Userクラスに属する汎用的な機能」としてクラスメソッドにしています。
2. User.new_token (トークン生成)
こちらも同様に、
- 乱数によって「新しいランダムなトークンを作る」だけです。
- 特定のユーザーの情報や状態に依存していません。
つまり、ユーザーが存在しているかどうかとは無関係にトークンを生成できます。
rememberメソッド
一方で、remember
メソッド は「特定のユーザー(インスタンス)を対象にして」、そのユーザーが次回訪問したときにログインを継続させる仕組みを準備する役割を持っています。
具体的には、
- トークンを生成して 自身(self)のインスタンス変数に保存 します。
- さらにそのユーザーの情報を データベースに保存(update_attribute) します。
つまり、remember
メソッドは、各ユーザーの状態に依存しているので、インスタンスメソッドにする必要があります。
- ユーザーAのトークンと、ユーザーBのトークンは異なるものが生成されます。
- それぞれ異なるトークンを各ユーザーのデータベースに保存するため、「特定のユーザーの状態」を前提にしています。
このように、「特定のユーザー」を意識している場合はインスタンスメソッドになります。
まとめ
- クラスメソッド: クラス全体に共通する、インスタンス(今回はユーザー)に依存しない処理
- インスタンスメソッド: 特定のインスタンスごとの処理や状態を扱う
勉強では学んだものの、実際のコードをいざ見てみると使い分けって難しいなと感じました。