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

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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/4b3ef667-a11d-ebf2-7f50-f2622124b7ae.gif)

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

### 1. 所属するクラス

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

```ruby
puts person.class
```

### 2. インスタンス変数

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

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

```ruby
puts person.instance_variables
```

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

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

```ruby
puts person.class.instance_methods(false)
```

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

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

```ruby
puts person.greeting
```

この呼び方はできない

```ruby
puts greeting
```

- 検証デモ
![object.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/a3bbf0ab-dad8-3e43-5a9a-25b2d435b655.gif)


- イメージ図　- インスタンスメソッドを呼び出すとき
![hito.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/c42bee74-35f0-14ff-c21c-48dc4e274ef8.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
```

```ruby
# 出力結果
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
```

```ruby
# 出力結果
"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
```

```ruby
# 出力結果
"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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/51c52212-79ce-9bc5-af1b-e7f9b8e31413.gif)


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

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

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

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

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

```ruby
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/4f9264b3-20e5-76e2-1f5d-b4bd524fe1f8.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/4a32b505-1834-0d17-935a-f3fea0a36c7f.gif)

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

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

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

Personのインスタンスから直接呼ぶとチョット違和感ありませんか!...?
- いきなり真面目な顔で 「人間五人作れ」 という人がいたら太郎さんは困ります。
![hito.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/c11a5fe0-02ed-1d0c-0d4d-a40d12928e64.png)

- Monsterクラスのインスタンスを参照中、そのインスタンスに他のクラスに関する処理をまとめてしてくれ！と言うのもチョット違和感ありませんか...？
![monster.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/e31ad565-2cf7-0858-9481-b1aaf3d059ec.png)


- Personクラスに直接頼みましょう。その方がソースコードの管理がしやすいでしょう。
![hito.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/ce507ad4-7c22-0078-a60e-cfe26096708c.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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/f8fc3e10-950a-ffef-0cee-2618c472d744.gif)

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

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

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

## ６章 クラスオブジェクト - Rubyにおいてクラスはオブジェクトだった？！🤔

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

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

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

```ruby
puts Person.class
# Class
```

「Personクラスは`Classクラス`に所属しているオブジェクト」ということが分かりました。
>* Classクラス自体もクラスに属しているのですがこれについては説明を省きます。

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

```ruby
Class.new
```

### クラスは実は定数

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

```ruby
Person = Class.new 
```

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

```ruby
class Person
end
```

実は

```ruby
Person = Class.new
```

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

「Rubyではクラスは定数なんだよ！」の意味がわからん...😑
と思っていた方はその事実に`(つд⊂)ｺﾞｼｺﾞｼｺﾞｼ（；ﾟДﾟ)…ハッ ！？`となったのではないでしょうか？

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

* 検証デモ (Personクラスをclassを使わず、定数で定義してみた)
![object.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240416/9be866f6-5f86-7390-d7b4-5d61ef756dca.gif)

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

## ７章 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/