クラスメソッドとインスタンスメソッドの使い分け(Ruby)

クラスメソッドとインスタンスメソッドの使い分け、初心者は絶対悩むところ。

いろんな本や記事を読みあさってようやく理解できてきたので書いておく。

基本はインスタンスメソッドを使う

インスタンスとは、オブジェクトのこと。オブジェクト名.classでクラス名を確認してみればわかるように、オブジェクトは必ず何らかのクラスに属している。前にこんな記事も書いた。

masuyama13.hatenablog.com

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クラスというオブジェクトだけが使えるメソッド、つまり特異メソッドを定義しているということである。

このメソッドを呼び出すとき、クラス名.メソッド名になるのもそういうわけだ。

ここまで理解すると、クラスメソッドとインスタンスメソッドの使い分けもわかる気がしてくる。

クラスメソッドの使いどころ

クラスは、「製造機」のイメージだと以前書いた。

masuyama13.hatenablog.com

製造機自身がやるべき仕事なら、クラスメソッドにするべきだ。

新しいメソッドを作る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クラスのインスタンスを作れる。こういう仕事がクラスメソッドの仕事だ。

まとめ

ということで、基本的にはインスタンスメソッドでできないかを考える。

クラスメソッドは、外部データを受け取って新たにインスタンスを生成する用途(ファクトリメソッド)で使う。

こちらの記事・書籍がとても参考になりました。