インスタンスメソッドとクラスメソッドの違いに入門してみる その1
久しぶりです! 引き続き
プロを目指す人のための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メソッドで一括管理した方が、他の人が見たときにわかりやすくなります。
続きは、明日公開します。