adventカレンダー初日の担当をさせていただきます !
DMM WEBCAMPでメンターをしているtanidaです !
Rubyのオブジェクトとは何か? オブジェクト指向とは何か?
そんな疑問に答えていければと思います。
ではでは、オブジェクトの世界を覗いてみましょう!
そもそもRubyにおいてオブジェクトとは(出落ち)
オブジェクト(インスタンス)とは、あるクラスから生成されたデータのこと!
*オブジェクトとインスタンスは同じモノだと、とりあえず覚えてください。
オブジェクト指向って何なんですか?
プログラムはオブジェクト間のメッセージのやりとり
という考え方です。
この考え方を理解するためにも、オブジェクトについて理解を深めていきましょう。
1章 実際にオブジェクトを作ってみる🤔
PCにRubyのインストールされてない方はこのサイトのEditorにソースコードを書いて試せます。
https://try.ruby-lang.org/
Personクラス - 人クラス
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">
がターミナル上で確認できました。
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
3章 アクセスメソッドの利用🤔
オブジェクトの外部からインスタンス変数を直接参照したり、インスタンス変数の値を更新することはできません。
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メソッド内だとインスタンス変数にアクセスできています。
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"
更新もしてみましょう。
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
を追加するとオブジェクトから直接インスタンス変数の値を「参照」したり、「設定」したりできます。
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 # 参照
*ちなみに他のオブジェクト指向言語でも、getterとsetterというワード、概念はあります。
4章 重要な概念レシーバーとは🤔
メソッドの戻り値を受け取る対象のことです。
2章の検証デモで、Personクラスにあるインスタンスメソッドgreetingを呼び出した時に
"Hi! I'm mentor taro" といった文字列が戻り値となっていました。
その戻り値を受け取る対象をレシーバーと言います.
先ほどのソースコードでの person がレシーバーとなります。
puts person.greeting # "Hi! I'm mentor taro" というメッセージをpersonが受け取る。
つまり、レシーバーとはインスタンスメソッドを呼び出しているインスタンス自身のこと !
もう少しレシーバーについて詳しくみてきましょう。
呼び出されたメソッドのレシーバー自身を参照する - self
インスタンスメソッド内
でselfと記述すると、そのメソッドを呼び出したレシーバを取得できます。
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
オブジェクト指向の世界では
「オブジェクトにセッメージを送り」 そして 「オブジェクトがメッセージを受け取る」
このように捉えられるのでレシーバーと呼ぶらしいです。
オブジェクト指向プログラミングは
プログラムはオブジェクト間のメッセージのやりとり
、
`「オブジェクトのメッセージングによるプログラミングの手法」
と言われる理由が少しイメージできたのではないでしょうか?
5章 クラスメソッドとは🤔
レシーバがインスタンスではなくクラスそのものであるメソッドの事です。
書き方は色々とあるのですがこの記事ではself.メソッド名
で定義します。
*先ほどのselfとは異なりインスタンスメソッド内
ではなくクラス内
に定義することになります。
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
じゃあ、クラスメソッドをどういう時に利用するの?
と言われると(まぁ好きなように使えるのですが)
インスタンスからメソッドを呼び出すと意味がちょっと違和感がある場合
や、
あるクラスのオブジェクトを参照中に他のクラスに関する処理を行いたい時
とかはクラスメソッドにすると良いかと思います。そのほうがソースコードの管理が楽になるはずです。
例えば、5人分のPersonインスタンスを一括で生成したい時...
Personのインスタンスから直接呼ぶとチョット違和感ありませんか!...?
では実際にbulk_createというクラスメソッドで5人分のPersonインスタンスを生成してみます。
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クラス
ということです。
という事でクラスオブジェクトがレシーバーになる
メソッドがクラスメソッド
と言うことになります。
なるほどぉ〜🤔💭 ん? チョット待って...クラス「オブジェクト」
...?
オブジェクト
ってあるクラスから生成されたデータ
、インスタンス
の事って言ってましたよね...?
クラス
とオブジェクト
は別物なんでちゃんと「クラス」
って言った方がいいんじゃないんですか?😒
うん、うん、そうそう... うん、オブジェクトはクラスに所属してるんだけどね...
まぁそうなんだけどね... 実は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クラスのオブジェクト(インスタンス)を定数に割り当てること」
に等しかったってことか...🤯
前章のコードと同じように動作しました。
これでクラスはオブジェクトと言うことが分かりました。
*なのでインスタンスの特徴であるインスタンス変数の保持や、インスタンスメソッドを呼び出すことも可能です。
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/