麻布十番で働くデータ分析者のブログ

グロースハック、プログラミング、データ分析の色々を発信します

インスタンスメソッドとクラスメソッドの違いに入門してみる その1

f:id:nimi0370376:20171218004350p:plain

久しぶりです! 引き続き

プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)

をやっていきます。 今日からオブジェクト指向とクラスの話になるので、少し時間がかかりました。

特に自分の中でもクラスメソッドとインスタンスメソッドの使い分けがいまひとつで、そこを整理するためにブログに残しておきたいと思います。

オブジェクト指向の入門

オブジェクト指向のクラス

オブジェクト指向のプログラミングでクラスと呼ばれたら、「オブジェクトの設計図」や「オブジェクトのひな形」と呼ばれます。

Rubyではオブジェクトは何らかのクラスに属しています。

例えば1という数字は、Integerクラスに属しています。

irb(main):001:0> 1.class
=> Integer

またRubyではオブジェクトのことを度々インスタンスと呼びます。

インスタンスメソッドというのはインスタンスに対するメソッドという意味です。

# 「Aliceさん 20才」というユーザーのオブジェクトを作成
alice = User.new('Alice',20)

オブジェクトはしばしば外部から取得したり変更したりしたい場合が出てきます。

例えば以下のコードはuserの名前を外部から取得したり、変更できるようにしています。

class User
    # first_nameの読み書きを許可する
    attr_accessor :first_name
end

user = User.new('Alice',20)
user.first_name #=> "Alice"

# first_nameを変更
user.first_name = 'アリス'
user.first_name #=> "アリス"

Rubyのクラスについて

オブジェクト指向について概ね把握したところで、今まで自分がよくわかってなかったクラスについてメモしておきます。

initializeメソッド

initializeメソッドは、インスタンスを初期化するために実行したい処理があれば、予め設定しておけるメソッドです。

また、予め設定したい情報は引数で設定することができ、newするときには引数の数を合わせる必要が出てきます。

class User
    def initialize(name, age)
        @name = name
        @age = age
    end
end

User.new('alice',20)

ちなみに上記の@nameや@ageをインスタンス変数と言います。

普通の変数と違い、入れ物のような役割を果たしてくれます。

また、nameやageをローカル変数といい、直接呼び出すことも可能です。

アクセサメソッド

インスタンス変数の値を読み書きするメソッドのことをアクセサメソッドと呼びます。

例えば下記のコード

class User
    attr_accessor :name

    def initialize(name)
        @name = name
    end

   # nameメソッドを明示的に定義する必要はない

end

user = User.new('alice')
# @nameを変更
user.name = 'bob'
user.name #=> "bob"

:nameがアクセサメソッドになっています。

クラスメソッド

クラスに関連が深いが、一つ一つのインスタンスに含まれるデータを使わないメソッドを定義したいときは、クラスメソッドを使います。

例えば、Userクラスのインスタンスを配列にして返すメソッドを定義してみましょう。

class User
  def initialize(name)
    @name = name
  end

  # クラスメソッドを定義する
  def self.create_user(names)
    names.map do |name|
      User.new(name)
    end
  end

  def hello
    "Hello,I'm #{@name}"
  end
end

names = ['alice','Bob','John']

#クラスメソッドを呼び出す
users = User.create_user(names)
users.each do |user|
  puts user.hello
end

次のような結果を返します。

Hello,I'm alice
Hello,I'm Bob
Hello,I'm John

メソッドに大きく分けて、クラスメソッドとインスタンスメソッドがあることを覚えておきましょう。

クラスメソッドを使うか、インスタンスメソッドを使うか

僕が最も理解し難かったのがクラスメソッドとインスタンスメソッドの違いです。

何となくはわかりますが、どちらを使うかがいまいちわかりませんでした。

たどり着いた結論は次の2点です。

  • インスタンスメソッドで基本は実装する
  • データを持たせるのがクラスというオブジェクトか、各々のオブジェクトか

インスタンスメソッド実装の利点

インスタンスメソッドで実装していくことの利点は、渡すべきデータを意識しなくて良いことになります。

インスタンス変数にデータを渡しておけば、@で参照できるのでいちいち引数をつける必要がなくなるのです。

  def initialize(title, keyword, description, category, body)
    @title       = title
    @keyword     = keyword
    @description = description
    @category    = category
    @body        = body
  end

  def render
    <<-EOS
<article>
  <h1>#{@title}</h1>
  <p>#{@keyword} #{@description} #{@category}</p>
  <p>#{@body}</p>
</article>
    EOS
  end

例えば上記のコードはインスタンスメソッドで実装がされています。

このコードをクラスメソッドで行います。

def self.render(title, keyword, description, category, body)
    <<-EOS
<article>
  <h1>#{title}</h1>
  <p>#{keyword} #{description} #{category}</p>
  <p>#{body}</p>
</article>
    EOS
  end

これ1回だとあまり恩恵を感じませんが、10回呼び出したいときはインスタンスメソッドの方が良いということになりますね。

データを持つ主体

クラスメソッドとインスタンスメソッドを考えるもう一つにデータを持つ主体を考えるということがあります。

クラスメソッドで実装するとデータは恐らく、該当クラス以外のところで管理されるか、もしくはスクリプトを実行するときの引数になるのでしょう。

そうするとデータの管理を自分で行っていく必要があるので、大変になります。

initializeメソッドで一括管理した方が、他の人が見たときにわかりやすくなります。

続きは、明日公開します。