54
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Ruby オブジェクトってなんなの?😑に答える

Last updated at Posted at 2020-11-30

adventカレンダー初日の担当をさせていただきます !
DMM WEBCAMPでメンターをしているtanidaです !

Rubyのオブジェクトとは何か? オブジェクト指向とは何か?
そんな疑問に答えていければと思います。

ではでは、オブジェクトの世界を覗いてみましょう!

そもそもRubyにおいてオブジェクトとは(出落ち)

オブジェクト(インスタンス)とは、あるクラスから生成されたデータのこと!

*オブジェクトとインスタンスは同じモノだと、とりあえず覚えてください。

オブジェクト指向って何なんですか?

プログラムはオブジェクト間のメッセージのやりとり という考え方です。
この考え方を理解するためにも、オブジェクトについて理解を深めていきましょう。

1章 実際にオブジェクトを作ってみる🤔

PCにRubyのインストールされてない方はこのサイトのEditorにソースコードを書いて試せます。
https://try.ruby-lang.org/

Personクラス - 人クラス

object.rbというファイルを作成して以下のソースコードを実行してみます。

object.rb
class Person
  # initializeはオブジェクトの初期化のための特別なメソッド
  # クラス名.new()した時の引数が渡されます
  def initialize(name)
    @name = name
  end
  
  def greeting
    "Hi! I'm #{@name}!"
  end
end

# Personインスタンスを生成した後、initializeメソッドが呼び出されます。
# これがオブジェクト(インスタンス)です。
person = Person.new('mentor taro')
p person

#<Person:0x00007fbd8686d490 @name="mentor taro"> がターミナル上で確認できました。

  • 検証デモ
    object.gif

2章 もう少しオブジェクト(インスタンス)について調べてみる🤔

1. 所属するクラス

インスタンスはクラスに所属しているのでclassメソッドを利用すると、オブジェクトが所属するクラスはどこか教えてくれます。

puts person.class

2. インスタンス変数

インスタンス変数とはインスタンスが保持している変数の事です。
先ほどのコードでいうと、@name です。インスタンス変数は@マークがイニシャルでなければいけません。
インスタンス変数は同じインスタンス内であればその値を参照、変更ができます。

instance_variablesメソッドを利用するとインスタンス変数を教えてくれます。

puts person.instance_variables

3. インスタンスメソッド

インスタンスメソッドとは、クラス内に定義された、
クラスから生成されたオブジェクト(インスタンス)からしか呼び出せないメソッドのこと。

puts person.class.instance_methods(false)

instance_methodsの引数にfalseを付け加えると、
継承先のメソッドは表示しないようにしてくれます。(継承については説明を省きます)
今回はPersonクラスに定義されたインスタンスメソッドのみを聞きたいのでそうしています。

Personクラスから生成されたオブジェクト(インスタンス)からならgreetingを呼べます。

puts person.greeting

この呼び方はできない

puts greeting
  • 検証デモ
    object.gif

  • イメージ図 - インスタンスメソッドを呼び出すとき
    hito.png

3章 アクセスメソッドの利用🤔

オブジェクトの外部からインスタンス変数を直接参照したり、インスタンス変数の値を更新することはできません。

object.rb
class Person
   # インスタンス変数@nameの値の参照、設定がnameというメソッドで可能になる。
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def greeting
    "Hi! I'm #{@name}"
  end
end

p @name
# 出力結果
nil

メソッドを呼び出して、インスタンス変数の値を取得する。

でも、オブジェクトの外部から、簡単にインスタンス変数の値にアクセスしたいし、更新もしたい。。。
しかし、アクセスメソッドなら実現できます。
実はもうそれは既にやっているのですが、greetingメソッド内だとインスタンス変数にアクセスできています。

object.rb
class Person
  def initialize(name)
    @name = name
  end

  def greeting
    "Hi! I'm #{@name}"
  end

  def name
   @name
  end
end

p Person.new("mentor taro").greeting
puts 
p Person.new("mentor jiro").name
# 出力結果
"Hi! I'm mentor taro"
"mentor jiro"

更新もしてみましょう。

object.rb
class Person
  def initialize(name)
    @name = name
  end

  def greeting
    "Hi! I'm #{@name}"
  end

  def name=(name)
    @name = name
  end
end

person = Person.new("mentor taro")

person.name = "mentor jiro"

p person.greeting
# 出力結果
"Hi! I'm mentor jiro"

attr_accessor (いわゆるgetter/setter)

attr_reader(getter) と attr_writer (setter) が融合したもの

attr_accessor を追加するとオブジェクトから直接インスタンス変数の値を「参照」したり、「設定」したりできます。

object.rb
class Person
  # インスタンス変数@nameの値の参照、設定がnameというメソッドで可能になる。
  attr_accessor :name

  def initialize(name)
    @name = name
  end
  
  def greeting
    "Hi! I'm #{@name}"
  end
end

person = Person.new('mentor taro')

puts person.name # 参照
person.name = 'mentor jiro' # 設定
puts person.name # 参照
  • 検証デモ
    object.gif

*ちなみに他のオブジェクト指向言語でも、getterとsetterというワード、概念はあります。

4章 重要な概念レシーバーとは🤔

メソッドの戻り値を受け取る対象のことです。

2章の検証デモで、Personクラスにあるインスタンスメソッドgreetingを呼び出した時に
"Hi! I'm mentor taro" といった文字列が戻り値となっていました。

その戻り値を受け取る対象をレシーバーと言います.
先ほどのソースコードでの person がレシーバーとなります。

puts person.greeting # "Hi! I'm mentor taro" というメッセージをpersonが受け取る。

つまり、レシーバーとはインスタンスメソッドを呼び出しているインスタンス自身のこと !

もう少しレシーバーについて詳しくみてきましょう。

呼び出されたメソッドのレシーバー自身を参照する - self

インスタンスメソッド内でselfと記述すると、そのメソッドを呼び出したレシーバを取得できます。

object.rb
class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end
  
  def greeting
    "Hi! I'm #{@name}"
  end

  def check_receiver
    # selfでレシーバーを取得
    # 言い換えれば、インスタンスメソッドの呼び出し元のオブジェクトを取得する
    puts self
    # レシーバーのnameも取得
    puts self.name
  end
end

person = Person.new('mentor taro')
person.check_receiver
  • 検証デモ
    object.gif

オブジェクト指向の世界では
「オブジェクトにセッメージを送り」 そして 「オブジェクトがメッセージを受け取る」
このように捉えられるのでレシーバーと呼ぶらしいです。

オブジェクト指向プログラミングは
プログラムはオブジェクト間のメッセージのやりとり
`「オブジェクトのメッセージングによるプログラミングの手法」
と言われる理由が少しイメージできたのではないでしょうか?

5章 クラスメソッドとは🤔

レシーバがインスタンスではなくクラスそのものであるメソッドの事です。
書き方は色々とあるのですがこの記事ではself.メソッド名で定義します。
*先ほどのselfとは異なりインスタンスメソッド内ではなくクラス内に定義することになります。

object.rb
class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end
  
  def greeting
    "Hi! I'm #{@name}"
  end

  # クラスメソッド
  def self.person_class_method
    "クラスメソッドの戻り値ですよ〜"
  end
end

person = Person.new('mentor taro')
puts Person.person_class_method

# インスタンスからはクラスメソッドは呼び出せない
puts person.person_class_method
  • 検証デモ
    object.gif

じゃあ、クラスメソッドをどういう時に利用するの?

と言われると(まぁ好きなように使えるのですが)
インスタンスからメソッドを呼び出すと意味がちょっと違和感がある場合や、
あるクラスのオブジェクトを参照中に他のクラスに関する処理を行いたい時 とかはクラスメソッドにすると良いかと思います。そのほうがソースコードの管理が楽になるはずです。

例えば、5人分のPersonインスタンスを一括で生成したい時...

Personのインスタンスから直接呼ぶとチョット違和感ありませんか!...?

  • いきなり真面目な顔で 「人間五人作れ」 という人がいたら太郎さんは困ります。
    hito.png

  • Monsterクラスのインスタンスを参照中、そのインスタンスに他のクラスに関する処理をまとめてしてくれ!と言うのもチョット違和感ありませんか...?
    monster.png

  • Personクラスに直接頼みましょう。その方がソースコードの管理がしやすいでしょう。
    hito.png

では実際にbulk_createというクラスメソッドで5人分のPersonインスタンスを生成してみます。

object.rb
class Person
  attr_accessor :name, :age

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

  def self.bulk_create(data)
    # mapでは各要素に処理を加えてその値を含んだ新しい配列を作ってくれています。
    data.map { |d| Person.new(d[:name], d[:age]) }
  end
end

# Hashの配列を定義
data = [
  {name: 'mentor 一郎', age: 10},
  {name: 'mentor 二郎', age: 20},
  {name: 'mentor 三郎', age: 30},
  {name: 'mentor 四郎', age: 40},
  {name: 'mentor 五郎', age: 50}
]

puts Person.bulk_create(data)
puts
p Person.bulk_create(data)

Person.bulk_create(data) でクラスメソッドを呼び出すと、
五人のPersonオブジェクト(インスタンス)が格納された配列が戻り値となっています。
その配列を受け取っているレシーバーはPersonクラスということです。

  • 検証デモ
    object.gif

という事でクラスオブジェクトがレシーバーになるメソッドがクラスメソッドと言うことになります。

なるほどぉ〜🤔💭 ん? チョット待って...クラス「オブジェクト」...?
オブジェクトってあるクラスから生成されたデータインスタンスの事って言ってましたよね...?
クラスオブジェクトは別物なんでちゃんと「クラス」って言った方がいいんじゃないんですか?😒

うん、うん、そうそう... うん、オブジェクトはクラスに所属してるんだけどね...
まぁそうなんだけどね... 実はRubyでは「クラスもオブジェクト」なんです... ( 'ω' )

6章 クラスオブジェクト - Rubyにおいてクラスはオブジェクトだった?!🤔

クラスのクラス - その名は Classクラス ! (頭痛くなりますねわかります)

ここまでは、オブジェクト(インスタンス)クラスから生成されるデータと説明してきましが、なんとクラスもなんです。
実は、先ほどまで利用していたPersonクラスは実はオブジェクト(インスタンス)なのです !

オブジェクトの後にclassメソッドをつけると所属先のクラスを知ることができましたよね。
プログラムを実行して確認しましょう。

puts Person.class
# Class

「PersonクラスはClassクラスに所属しているオブジェクト」ということが分かりました。

  • Classクラス自体もクラスに属しているのですがこれについては説明を省きます。

PersonクラスはClassクラスのインスタンスということなので、Personクラスの実態は以下になります。

Class.new

クラスは実は定数

PersonクラスClass.newと分かりました。
では、Personという名前はどこからくるのでしょう...? 答えは定数です。
定数は覚えてますでしょうか?アルファベット大文字 ([A-Z]) で始まる識別子ですね。
以下のようにClass.new定数Personに割り当てます。

Person = Class.new 

今まで見てきた以下のコードは

class Person
end

実は

Person = Class.new

だったということになります。!(◎_◎;)

「Rubyではクラスは定数なんだよ!」の意味がわからん...😑
と思っていた方はその事実に(つд⊂)ゴシゴシゴシ(;゚Д゚)…ハッ !?となったのではないでしょうか?

今まで見てきたclassの実態「Classクラスのオブジェクト(インスタンス)を定数に割り当てること」に等しかったってことか...🤯

  • 検証デモ (Personクラスをclassを使わず、定数で定義してみた)
    object.gif

前章のコードと同じように動作しました。
これでクラスはオブジェクトと言うことが分かりました。
*なのでインスタンスの特徴であるインスタンス変数の保持や、インスタンスメソッドを呼び出すことも可能です。

7章 Railsで利用してる身近なオブジェクト(一部を抜粋)🤔

1. モデル

わかりやすいところでいうとモデルですね。
Railsのmodelってclassって書いてありますよね。
model名.new とかはクラスからインスタンスを生成しているってことです。
model内にインスタンスメソッドを定義してるなら、インスタンスからそのメソッドを呼び出せるわけです。

2. コントローラー

controllerも見てみるとclassって書いてありますよね。
例えば、HogeControllerのindexアクションを実行するって事は、
HogeControllerクラスから生成されたインスタンスからインスタンスメソッドindexを実行している訳です。

先ほどRubyのクラスは定数ということが分かりました。
Railsのエラー文でも定数(constant)というワードが出てきます。
uninitialized conostant コントローラー名 っていうエラーは見たことあるでしょうか?
マッチしたUrlのパスの行き先のコントローラーが探索できなかった時に出るエラーですね。
Routingの get '/users', to: 'users#index' がマッチした時に UsersController が探索できないような時です。(よく誤字とかで)

コントローラーをちゃんと定義できてないという趣旨のエラー文が何故
初期化されてない定数 コントローラー名 ってエラー文になるねん😑という疑問は
先ほどのRubyのクラスは定数だということを知って納得できたのではないでしょうか?

とりあえず、Railsを利用している時もあちこちでオブジェクトが利用されているんだな! ということだけ知っていればひとまずOKです!

まとめ

Rubyのオブジェクトをこれまで紹介してきました。

ざっくりまとめると
オブジェクトはクラスから生成されるインスタンスで、
Rubyはオブジェクトの世界で成り立っているんだなぁ〜。
「オブジェクトのメッセージングによるプログラミング手法」 がオブジェクト指向なんだなぁ〜。
ということです。

Rubyを通してまだまだ知れることがあります。
今回の話題に関するものとして、特に継承moduleについて調べてみると良いでしょう。
実際に手を動かして、実験して理解を深めましょう !!!

もし参考になったらいいねもよろしくです ☕️( ˙౪˙)💻 (疲れたので一服)

参考

書籍 たのしいRuby
https://www.ruby-lang.org/ja/
https://docs.ruby-lang.org/ja/2.7.0/doc/index.html
https://railsguides.jp/
...etc

引用画像
イラストや
https://www.irasutoya.com/

54
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?