Ruby
初心者
メソッド
68
どのような問題がありますか?

この記事は最終更新日から1年以上が経過しています。

投稿日

更新日

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

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

注意

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

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

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

クラスを作る

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

最初は最低限の機能
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人です。入学させるためにインスタンスメソッドを作成し、入学させましょう。

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組と森岡先生がいらっしゃいますのでクラスを追加しましょう。
また、クラス名簿を出力するメソッドも追加します。これはインスタンスメソッドでしょうか。クラスメソッドでしょうか。

クラス名簿を出力
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()
# > ["鳴宮", "竹早", "山之内", "如月", "小野木"]

クラス名簿=クラスごとの名簿を出力する。のでインスタンスメソッドになります。

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

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

全校生徒に関わる処理追加
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と触れ合いました。文法や規約などに違反していた場合はコメント欄へ記述していただけると大変うれしく思います。

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
ユーザー登録ログイン
right1121
Railsを勉強してエンジニアの世界に入ったら、いつの間にかPython使いになってました。
この記事は以下の記事からリンクされています

コメント

(学校の)クラスを扱う(Ruby の)クラスを Class という名前にしていますが,Ruby にはもともと組み込みで Class という名前のクラスが存在しています。
よって,この記事のコードは Class というクラスを新規作成しているのではなく,既存のクラスを上書きしてしまっています。ちょっと危険かなあ,と思いました。

Ruby 組み込みの Class というクラスは,〈クラスのクラス〉です(なんのこっちゃ?)
Ruby ではクラスもまた一つのオブジェクトですよね。オブジェクトであるということは,何らかのクラスのインスタンスであるわけです。その「何らかのクラス」の正体が Class です。

すべてのオブジェクトは,class というメソッドを持っていて,そのオブジェクトが属するクラスを返してくれます。
確かめてみましょう。

p "abc".class # => String
p 123.class # => Integer

これをクラスについてもやってみると・・・

p String.class # => Class
p Integer.class # => Class
0

クラスを Class という名前にしていますが,Ruby にはもともと組み込みで Class という名前のクラスが存在しています。
Class というクラスを新規作成しているのではなく,既存のクラスを上書きしてしまっています。

おっしゃる通りです。私としても自覚はあります。
この記事の目的は、クラスの使用方法をわかりやすく伝えることです。
わかりやすさ=>全員が知っている知識=>義務教育である学校をたとえに使用
という経緯でClassという名称を使いました。

ただ、@scivolaさんのご指摘通り実際には使わないほうが良いという警告を追記しました。
(たとえが悪いと言われればそれまでですが)

1

今、インターン生として働いていて、クラスメソッドとインスタンスメソッドをしっかり理解していなかったので勉強になりました。
とてもわかりやすい例えでした!

1

クラスメソッドとインスタンスメソッドがなかなか概念的に理解できなくて調べていたらこの記事にたどり着きました。非常にわかりやすい例えで理解するのに役立ちました!

1

いい記事ありがとうございます!
ただ、オブジェクト指向が5000%理解できる記事的にいうと、クラスが全校生徒返すのって違和感あるかもしれないです。

1
どのような問題がありますか?
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
68
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー