クラスメソッドとインスタンスメソッドの使い分け、初心者は絶対悩むところ。
いろんな本や記事を読みあさってようやく理解できてきたので書いておく。
基本はインスタンスメソッドを使う
インスタンスとは、オブジェクトのこと。オブジェクト名.class
でクラス名を確認してみればわかるように、オブジェクトは必ず何らかのクラスに属している。前にこんな記事も書いた。
Rubyはオブジェクト指向言語なので、基本的にはインスタンスを作って、そのインスタンスに対してインスタンスメソッドを使って仕事をさせる。
では、クラスメソッドはどういうときに使うのか?を考える前に、特異メソッドについて理解しなければならない。
特異メソッドとは
特異メソッドとは、特定の一つのオブジェクト(インスタンス)だけが使えるメソッドのこと。
定義方法は、以下の通り。
def オブジェクト名.メソッド名 # 処理 end
ここで書いたオブジェクト名のオブジェクト(インスタンス)だけが、そのメソッドを使うことができる。
たとえば、こんな感じ。
def my_square.area puts "私の四角の面積は、#{length * width}です。" end
上の場合だと、my_square
というオブジェクトだけが、このarea
メソッドを使うことができる(他のオブジェクトが呼び出すとエラーになる)。
クラスメソッドは特異メソッドの一種
クラスメソッドというのは、クラス(というオブジェクト)が持つ特異メソッドのことだ。
クラスメソッドで有名なのはnew
だろう。クラス名.new
で、そのクラスの新しいインスタンスを作る。ただ、このnew
はちょっと特殊なので、今は考えないでおく。
クラスメソッドの定義方法は、以下の通り。
class Area def self.メソッド名 # 処理 end end
クラス構文直下のself
は、クラス自身を指す(一方、インスタンスメソッド内でのself
はインスタンス自身を指す)。クラス名が後で変更になったときに楽なので、クラスメソッド定義では通常self
を使うが、下のようにクラス名を直接書くこともできる。
class Area def Area.メソッド名 # 処理 end end
これを見るとわかりやすいが、これはAreaクラスというオブジェクトだけが使えるメソッド、つまり特異メソッドを定義しているということである。
このメソッドを呼び出すとき、クラス名.メソッド名
になるのもそういうわけだ。
ここまで理解すると、クラスメソッドとインスタンスメソッドの使い分けもわかる気がしてくる。
クラスメソッドの使いどころ
クラスは、「製造機」のイメージだと以前書いた。
製造機自身がやるべき仕事なら、クラスメソッドにするべきだ。
新しいメソッドを作るnew
はまさにこれだ。
new
以外には、外部データを受け取って新たにインスタンスを作る用途で使われることが多いようだ。そういうメソッドのことをファクトリメソッドと呼んだりする。ファクトリ(factory)とは工場のことだから、まさに製造機のイメージ。
たとえば、こういうメソッド。
class Item attr_reader :name, :byte_size def initialize(entry) @name = entry @byte_size = File.stat(entry).size end def self.create_items(entries) entries.map { |entry| Item.new(entry) } end end
このItem製造機(Itemクラス)は、インスタンス(オブジェクト)を作る材料として1つのentryを必要とする。必要となる材料とは、new
したときに呼ばれるinitializeメソッドの引数である。
ただ、このentryは通常いくつかでまとまって配列に入った形で存在しているとする。Item製造機は配列を受け取れる仕様になっていないので、配列をItem製造機に入れてもItemオブジェクトをうまく作ることができない(エラーになる)。
そういうときには、配列のentriesを材料としてItemオブジェクトを作れるように製造機をカスタマイズしようと考える。これが「データを受け取って新たにインスタンスを作る」ということだ。
self.create_items
メソッドは、entriesを配列の形のまま受け取って各entryからItemクラスのインスタンスを作れる。こういう仕事がクラスメソッドの仕事だ。
まとめ
ということで、基本的にはインスタンスメソッドでできないかを考える。
クラスメソッドは、外部データを受け取って新たにインスタンスを生成する用途(ファクトリメソッド)で使う。
こちらの記事・書籍がとても参考になりました。