クラスメソッド、インスタンスメソッド。いつ使うのか。

まだエンジニアとして働く前、ちょうどRubyの勉強を始めた時に書いた[「メソッドとクラスメソッドとインスタンスメソッドが曖昧だった」](https://qiita.com/right1121/items/c74d350bab32113d4f3d)という記事があります。
この記事ですが、すでに1年半ほどが経過しているにも関わらず現在もいいねが押されており、初学者の助けになっているのであれば嬉しい限りです。
しかし、読み返してみるとこの記事は**概念を説明しているだけで実際にどのように使えばよいのか**は記述されておらず、腑に落ちないところがあるのかなと思いました。
そこで、今回はできる限り実例を交えて解説していきたいと思います。

## 注意

コードの中で`class Class`という形でclassを宣言しています。
ただし、これは非常に悪い例でRubyのClassというクラスを上書きしてしまっています。
あくまでわかりやすさを前提に説明しているので、実際にクラスを使用する際には上書きしないよう気を付けてください。

## いきなりですが、あなたは校長先生になります。

あなたにはいまから校長先生になっていただきます。生徒が入学してくるため、入学した生徒の管理を**Ruby**で行ってください。

### クラスを作る

まずはクラスを作成しましょう。クラス名は1年1組で、担当教師は山中先生です。

```ruby:最初は最低限の機能
class Class
  def initialize(teachername, grade, group)
    # gradeは学年、groupは組
    @teachername=teachername
    @grade=grade
    @group=group
  end
end

my_class = Class.new("山中", 1, 1)
```

とりあえずクラスが1つできました。

### 生徒を入学させる(入学メソッド作成)

生徒を入学させていきます。入学する生徒の数は5人です。入学させるために**インスタンスメソッド**を作成し、入学させましょう。

```ruby:5名が入学
class Class
  def initialize(teachername, grade, group)
    @teachername=teachername
    @grade=grade
    @group=group
    # 名簿を作成
    @roster=[]
  end

  def enter(name)
    # インスタンスメソッド
    @roster.push name
  end
end

my_class = Class.new("山中", 1, 1)
my_class.enter("平沢")
my_class.enter("秋山")
my_class.enter("田井中")
my_class.enter("琴吹")
my_class.enter("真鍋")
```

このように、**作成したクラスに何かアクションを行いたい場合はインスタンスメソッドを使用します。**

### 追加入学。そして名簿の出力

どうやら手違いがあったため、入学する生徒が5名追加となりました。
幸い、1年2組と森岡先生がいらっしゃいますのでクラスを追加しましょう。
また、クラス名簿を出力するメソッドも追加します。**これはインスタンスメソッドでしょうか。クラスメソッドでしょうか。**

```ruby:クラス名簿を出力
class Class
  def initialize(teachername, grade, group)
    @teachername=teachername
    @grade=grade
    @group=group
    @roster=[]
  end

  def enter(name)
    @roster.push name
  end

  def print_roster()
    # インスタンスメソッド？クラスメソッド？
    print @roster
  end
end

yamanaka_class = Class.new("山中", 1, 1)
yamanaka_class.enter("平沢")
yamanaka_class.enter("秋山")
yamanaka_class.enter("田井中")
yamanaka_class.enter("琴吹")
yamanaka_class.enter("真鍋")

morioka_class = Class.new("森岡", 1, 2)
morioka_class.enter("鳴宮")
morioka_class.enter("竹早")
morioka_class.enter("山之内")
morioka_class.enter("如月")
morioka_class.enter("小野木")

yamanaka_class.print_roster()
# > ["平沢", "秋山", "田井中", "琴吹", "真鍋"]
morioka_class.print_roster()
# > ["鳴宮", "竹早", "山之内", "如月", "小野木"]
```
クラス名簿=クラス**ごと**の名簿を出力する。のでインスタンスメソッドになります。

### 全校生徒の人数を出力したい

いきなり生徒数が増えましたが、インスタンスメソッドを作成していたためコードの修正は軽微なものでしたね。
ただ、今後も急な転校などが発生する恐れがあります。また、今でこそ全校生徒は少ないですが、今後は増えていくかもしれません。
そうなると全校生徒の人数を把握するのが大変です。
そこで、**クラスメソッドを使用して全校生徒数を出力してみましょう。**

```ruby:全校生徒に関わる処理追加
class Class
  # 全校生徒数をクラス変数で定義
  @@all_students_count = 0

  def self.print_all_students_count
    # クラスメソッドで全校生徒数を出力するメソッド追加
    print "全校生徒は#{@@all_students_count}人です"
  end

  def initialize(teachername, grade, group)
    @teachername=teachername
    @grade=grade
    @group=group
    @roster=[]
  end

  def enter(name)
    @roster.push name
    # 入学したら全校生徒数を+1
    @@all_students_count += 1
  end

  def print_roster
    print @roster
  end
end

Class.print_all_students_count
# > 全校生徒は0人です
# まだ入学者がいないため全校生徒は0人
yamanaka_class = Class.new("山中", 1, 1)
yamanaka_class.enter("平沢")
yamanaka_class.enter("秋山")
yamanaka_class.enter("田井中")
yamanaka_class.enter("琴吹")
yamanaka_class.enter("真鍋")

Class.print_all_students_count
# > 全校生徒は5人です
# 山中先生のクラスに入学したため5人

morioka_class = Class.new("森岡", 1, 2)
morioka_class.enter("鳴宮")
morioka_class.enter("竹早")
morioka_class.enter("山之内")
morioka_class.enter("如月")
morioka_class.enter("小野木")

yamanaka_class.print_roster()
morioka_class.print_roster()

Class.print_all_students_count
# > 全校生徒は10人です
# 森岡先生のクラスにも入学したため10人
```

まずはクラス変数で全校生徒数(`@@all_students_count`)を0で初期化しています。
そして、`enter`メソッドで入学後、全校生徒数に対して+1をするようにしました。これで入学者が増えると全校生徒数も増えていきます。
最後に、`self.print_all_students_count`のクラスメソッドを用意して全校生徒数を出力しています。
出力するタイミングを入学前、入学後、追加入学後に分けることによって挙動が確認できるかと思います。
このように、クラスメソッドは各クラス共通で使用できるメソッドであるといえます。

## まとめ

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

- インスタンス毎で挙動を制御したい場合に使用する
- 今回だと入学はクラス(学級)ごとに行うためインスタンスメソッドとなる

### クラスメソッド

- クラス全体で共通の挙動を行う際に使用する
- 今回だと全校生徒はClassが管理していた

## あとがき

途中から全校生徒はクラス管理なのか？と疑問が出てきてしまい、悩みながらも書き抜きました。非常に良い記事とは言いにくいところがありますが、メソッドの役割理解・分類の助けになればと思います。
また、最近はPythonばかり書いているので久々にrubyと触れ合いました。文法や規約などに違反していた場合はコメント欄へ記述していただけると大変うれしく思います。