Rubyの『クラス』関係の問題を集めてみて解いてみた。

#Rubyのクラスが分からない
チェリー本を読んでいざアウトプットしていこうと思ったものの、自分でプログラムを作るとなると中々解けずに難しい。。。

そんな僕と同じ悩みを抱えている方のために**基礎問題**を集めてみました。

基礎問題と言えど正直、曖昧な知識のままこの問題を解いていくと分からないことがあります。

出来れば答えを見ずに進めていくのが良いと思いますが、考えてもどうしても分からない場合は記事の下の方に回答を書いておきますので参考にして下さい。

そして、回答を読んでも分からないと言うことが僕自身ありましたので、分からなかった点まで記載していきます。

#問題（3問）
##①クラス変数とクラスメソッドについて
```sample.rb
class Car
  def self.run
    @@count += 1
  end

  def count
    @@count
  end
end

car1 = Car.new
car1.run

car2 = Car.new

car2.run
car1.run

puts Car.count
```

[こちらの記事の問題](https://www.oiax.jp/rails/ruby-ex/class_variable_and_class_method.html)を参照しました。

●＜問題＞
上のプログラムは実行するとエラーが出ます。
ある箇所を修正して期待通りの出力ができる様にソースを変更してください。

（期待出力）
・インスタンスメソッド```run```を呼ぶとクラスの共有カウンタが1ずつ増加する。
・クラスメソッド```count```を呼ぶと現在のカウンタの値が返される。

3と表示されればオッケーです。

●＜ヒント＞
以下の記事が、この問題を解く上で非常に参考になりました。
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709

##②特異メソッド
[この記事](http://www.minituku.net/courses/500228005/contents/228585781.html)を引用させていただきました。

```food.rb
class Food
  def eat
    puts "I like."
  end
end
 
natto = Food.new()
wasabi = Food.new()
karaage = Food.new()

natto.eat #=>I like.
wasabi.eat #=>I don't like.
karaage.eat #=>I love.
```
●＜問題＞
コメントアウト通りの出力になる様にコードを変えてください。

##③Musicクラスを継承したRapクラスを定義する
[こちらの記事](http://www.minituku.net/courses/500228005/contents/767771771.html)を参照しました。

●＜問題＞
Rapクラスへ記述するコードを下記の出力結果から予測してください。

```sample.rb
class Music
  def mc
    puts "This is #{@genre} of #{self.class.to_s}"
  end
 
  def initialize(genre)
    @genre = genre
  end
end

Rap.new("mc-battle").mc
```
（出力例）

```
This is mc-battle of Rap
Yo, mic check 1, 2.
```

#ー※※※※※※※※※※※解答※※※※※※※※※※※ー
##①クラス変数とクラスメソッドについて

クラス変数とクラスメソッドの使い方を理解していないと解けないですね。

●＜クラス変数＞
>
- クラスとそのインスタンスがスコープになる
- 定数と似ているがクラス変数は何度でも値を変更できる点で異なる
- クラスメソッド、インスタンスメソッド、クラス定義式内でアクセス可能

以下の記事が参考で、この問題を解く上で非常に参考になりました。
https://qiita.com/mogulla3/items/cd4d6e188c34c6819709

まず、見るべき点は**２、６行目**です。

```sample.rb
class Car
  def self.run #2行目
    @@count += 1
  end

  def count #6行目
    @@count
  end
end

car1 = Car.new
car1.run

car2 = Car.new

car2.run
car1.run

puts Car.count
```
気づくことはなかったでしょうか？

最後の行を見てください。
```puts Car.count```

これは、Carクラスcountメソッドをそび出しているみたいですが、おかしくありませんか？

クラスをそのまま呼び出す際は、クラスメソッド出なければいけません。

修正すると、

```sample.rb
class Car
  def self.run #2行目
    @@count += 1
  end

  def self.count #6行目
    @@count
  end
end

#-中略-

puts Car.count #最後の行
```
そして、2行目もおかしいですね。

こちらはインスタンスメソッドにしなければいけません。

```sample.rb
class Car
  def run #2行目/def self.runから変更
    @@count += 1
  end
end

car1 = Car.new
car1.run
```

なぜなら、classのそとでは```car1```とインスタンスを作成しているからそのインスタンスを呼び出すためのメソッドとして、インスタンスメソッドにしなければいけません。

しかしこれで終わりではありません。

**クラス変数 ```@@count``` が定義されていません。**

```sample.rb
class Car
  @@count = 0 #クラス変数の定義

  def run #クラスメソッド
    @@count += 1 #@@countはクラス変数
  end

  def self.count #インスタンスメソッド
    @@count
  end
end

car1 = Car.new
car1.run

car2 = Car.new

car2.run
car1.run

puts Car.count

# => $ ruby sample.rb 
# => 3
```
これでオッケーです。

##②特異メソッド
そもそも、特異メソッドとは？
**１つのインスタンス固有のメソッドのことを指します。**

特異メソッドは、```def オブジェクト名.メソッド名```で定義できます。
https://qiita.com/k-penguin-sato/items/d637dced7af32e4ec7c0

よって、

```food.rb
class Food
  def eat
    puts "I like."
  end
end
 
natto = Food.new()
wasabi = Food.new()
karaage = Food.new()

#--追加--↓

def wasabi.eat
  puts "I don't like."
end

def karaage.eat
  puts "I love."
end

#--追加--↑

natto.eat
wasabi.eat
karaage.eat
```

同じクラスのインスタンスであるオブジェクトに、同じ名前の動作が異なるメソッドを定義します。

これが特異メソッドで、これはクラスに対してではなくクラスからできたインスタンスに対してのメソッドになります。

よってそれぞれのメソッドからの出力ができます。

（結果）

```
I like.
I don't like.
I love.
```

##③Musicクラスを継承したRapクラスを定義する
```sample.rb
class Music
  def mc
    puts "This is #{@genre} of #{self.class.to_s}"
  end

  def initialize(genre)
    @genre = genre
  end
end

Rap.new("mc-battle").mc
```

・```self.class.to_s```：```Rap.new```の様に作られたインスタンス自体のクラス名を文字列に変換する。

では、```Rapクラス```と、を作っていきます。

```sample.rb
#--中略--
class Rap < Music
end
```
出力文字を見てください。
```Yo, mic check 1, 2.```

Musicクラスでは定義されていない出力ですので、Rapクラスで出力していきます。

最後の行の```Rap.new("mc-battle").mc```を見てみると、メソッドは```mc```が使われていますので、記載していきます。

```sample.rb
#--中略--
class Rap < Music
  def mc
  end
end
```
``Musicクラス``を継承しているので必要な部分を記載すると

```sample.rb
#--中略--
class Rap < Music
  def mc
    puts "Yo, mic check 1, 2."
  end
end
```
しかしこれで終わりではないです。

まだ、現時点ではMusicクラスの呼び出しができていません。

どうすればいいでしょうか？

**「super」メソッドを実行します。**

superメソッドは、**スーパークラスの中でその呼び出されたメソッドと同じメソッド名を持つメソッドを探して実行します。**

それでは、書き換えると

```sample.rb
class Music
  def mc
    puts "This is #{@genre} of #{self.class.to_s}"
  end
 
  def initialize(genre)
    @genre = genre
  end
end

class Rap < Music
  def mc
    super #追加コード
    puts "Yo, mic check 1, 2."
  end
end

Rap.new("mc-battle").mc
```
完成です。