LoginSignup
37
20

【Ruby】サトシになったつもりでクラスについてまとめてみた(初学者向け)

Last updated at Posted at 2023-12-04

はじめに

Ruby Silverの勉強をして身についた知識のなかで、クラスについての知識がRailsにも生きていると感じたので、まとめていきます。
Ruby Silver合格までの勉強方法は前回記事でまとめています。
【プログラミング初学者】Ruby Silverに合格したのでまとめました!

目次

タイトル
1 オブジェクト指向の基本的な単語の説明
2 クラスの定義とインスタンスの作成
3 インスタンスメソッドの定義
4 インスタンス変数とアクセサメソッド 
5 クラスメソッドとクラス変数の重要性 
6 クラスの継承 
7 最後に 

基本的な単語の説明

まずは、クラスを理解するための元となる単語についてまとめてみました。
以下の単語はクラスを理解する上で重要になってくると思いますので、説明していきます。

  • クラス
  • オブジェクト
  • インスタンス
  • レシーバ
  • メソッド

クラス (Class)

クラスはオブジェクト指向プログラミングのデータ型です。イメージしやすく言うと「オブジェクトの設計図」または「ひな形」として機能します。Rubyでは全てのオブジェクトが何らかのクラスに属しており(継承で説明)、同じクラスから作成されたオブジェクトは共通の属性やメソッドを持ちます。
以下のにクラスを作成します。

class Pikachu
 #Pikachu classを定義
end

オブジェクト、インスタンス、レシーバ (Object, Instance, Receiver)

クラスは設計図なので、実際に利用するにはオブジェクト(またはインスタンス)を生成する必要があります。同じクラスから作られたオブジェクトは同じ属性やメソッドを共有しています。オブジェクトのメソッドを呼び出す際、そのオブジェクトをレシーバと呼びます。

pikachu = Pikachu.new  # Pikachuクラスからpikachuというオブジェクトを生成
pikachu.attack #

ここで、attackメソッドを呼び出す際、pikachuオブジェクトがメソッドのレシーバーとなります。この場合、attackメソッドはpikachuオブジェクトに対して実行されます。(以下で詳しく説明)

メソッド、メッセージ (Method, Message)

オブジェクトが持つ「動作」や「振る舞い」をメソッドと呼びます。メソッドは何らかの処理をまとめ、再利用可能な形にしたものです。オブジェクトにメッセージを送ることで、対応するメソッドが実行されます。

class Pikachu
  def attack
    puts "10まんボルト"
  end
end
pikachu = Pikachu.new
pikachu.attack  # "10まんボルト"を表示

pikachu オブジェクトに attack メソッドを呼び出すことで、対応するメソッドが実行され、"10まんボルト"という文字列が表示されます。この例では、attack メソッドが Pikachu クラスに定義されているので、それがメッセージに対する応答として実行されています。
具体的な流れ

  1. pikachu オブジェクトが attack メソッドを呼び出します。
  2. attack メソッドが Pikachu クラス内に定義されているため、それが実行されます。
  3. 実行された結果、"10まんボルト"という文字列が表示されます。
    このように、オブジェクトにメッセージを送ることで、そのオブジェクトが持つメソッドが呼び出され、特定の処理が実行されます。

状態(ステート)、属性(アトリビュート、プロパティ) (State, Attribute, Property)

オブジェクトごとに保持されるデータを「オブジェクトの状態」または「ステート」と呼びます。この状態を構成する個々のデータを属性(アトリビュートやプロパティ)と呼び、外部から取得や変更が可能となってます。

class Pokemon
  attr_accessor :name  # name属性の読み書きを許可
  def initialize(name)
    @name = name
  end
  pikachu= Pokemon.new("ピカチュウ")
  puts pikachu.name  # "ピカチュウ"を表示
end

上記は、Pokemon クラスが name という属性(アトリビュート)を持ち、それを外部から読み書きできるように attr_accessor メソッドを使ってます。これにより、Pokemon クラスのインスタンスである pikachu オブジェクトが name 属性を持ち、初期化時に設定された値を取得できます。
具体的な流れ

  1. Pokemon クラスの定義で attr_accessor :name が宣言されているため、name 属性の読み書きが可能になります。
  2. initialize メソッドで、Pokemon クラスのインスタンス(この場合は pikachu オブジェクト)が生成され、初期化時に引数として渡された名前が @name 属性にセットされます。
  3. puts pikachu.name によって、pikachu オブジェクトの name 属性の値が表示されます。この場合、"Pikachu" が表示。
    以上のように、オブジェクトの状態は属性によって表現され、これらの属性は外部からアクセス可能です。 attr_accessor を使用することで、属性に対して読み書きのためのゲッターとセッターが自動的に生成されます。

クラスの定義とインスタンスの作成

Rubyにおいて、クラスの定義は以下のような構文を用います。また、そのクラスからインスタンスを作成するにはnewメソッドを使用します。

class Pokemon
  def initialize(name, level)
    @name = name
    @level = level
  end
  def show_info
    puts "Name: #{@name}, Level: #{@level}"
  end
end

この例では、Pokemonクラスが定義されています。このクラスはポケモンを表すもので、initializeメソッドでポケモンの名前とレベルを初期化します。また、show_infoメソッドでポケモンの情報を表示します。
クラスの定義ができたら、このクラスからインスタンスを作成します。

pikachu = Pokemon.new('ピカチュウ', 10)
pikachu.show_info
# 出力: Name: ピカチュウ, Level: 10

このようにして、Pokemonクラスからpikachuというインスタンスが作成され、その情報が表示されます。

インスタンスメソッドの定義

クラス内で定義されたメソッドは、そのクラスのインスタンスメソッドとなります。例えば、Pokemonクラスにlevel_upというメソッドを追加し、ポケモンのレベルを上げることができるようにしてみる。

class Pokemon
  # ... (前述のコード)
  def level_up
    @level += 1
    puts "#{@name} は レベル#{@level}にあがった!"
  end
end

このようにして新しいメソッドを追加したら、それを使ってインスタンスを操作できます。

pikachu = Pokemon.new('ピカチュウ', 10)
pikachu.show_info
# 出力: Name: ピカチュウ, Level: 10
pikachu.level_up
# 出力:  ピカチュウ は レベル11にあがった!
pikachu.show_info
# 出力: Name: ピカチュウ, Level: 11

インスタンス変数とアクセサメソッド

クラス内で利用する変数はインスタンス変数として定義され、外部からアクセスするためにはアクセサメソッドを使用します。

class Pokemon
  def initialize(name, level)
    @name = name
    @level = level
  end
  def show_info
    puts "Name: #{@name}, Level: #{@level}"
  end
  # アクセサメソッド
  def name
    @name
  end
  def level
    @level
  end
end

これにより、外部からnamelevelメソッドを使ってインスタンス変数にアクセスできます。

pikachu = Pokemon.new('ピカチュウ', 10)
puts "Name: #{pikachu.name}, Level: #{pikachu.level}"
# 出力: Name: ピカチュウ, Level: 10

上記を簡略化するためにRubyではattr_accessorを使用することができます。

class Pokemon
  attr_accessor :name, :level
  def initialize(name, level)
    @name = name
    @level = level
  end
  def show_info
    puts "Name: #{@name}, Level: #{@level}"
  end
end

これでattr_accessorによって、namelevelのゲッターとセッターが自動的に生成されます。
例として、クラスメソッドも加えてポケモンの例を作成してみます。クラスメソッドはクラス自体に関連する処理を行うメソッドで、クラスメソッドではインスタンス変数にアクセスできない特徴があります。

  • ポケモンクラスを拡張したコード例
class Pokemon
  attr_accessor :name, :level
  @@total_pokemons = 0  # クラス変数で総ポケモン数を管理
  def initialize(name, level)
    @name = name
    @level = level
    @@total_pokemons += 1  # インスタンスが生成されるたびに総ポケモン数を増やす
  end
  def show_info
    puts "Name: #{@name}, Level: #{@level}"
  end
  def level_up
    @level += 1
    puts "#{@name} は レベル#{@level}にあがった!"
  end
  def use_attack(attack_name)
    puts "#{@name}#{attack_name}  !"
  end
  def self.total_pokemons
    @@total_pokemons
  end
end

このクラスでは、@@total_pokemonsというクラス変数を導入し、クラスメソッド self.total_pokemons で総ポケモン数を取得できるようにしました。

  • 上記クラスを使った例。
# ポケモンの作成
pikachu = Pokemon.new('ピカチュウ', 10)
charmander = Pokemon.new('ヒトカゲ', 8)
# ポケモンの情報表示
pikachu.show_info
# 出力: Name: ピカチュウ, Level: 10
charmander.show_info
# 出力: Name: ヒトカゲ, Level: 8
# レベルを上げる
pikachu.level_up
# 出力: Pikachu は レベル11 にあがった!"
# 攻撃を使用
pikachu.use_attack('サンダーボルト ')
# 出力: ピカチュウ の サンダーボルト !
# クラスメソッドで総ポケモン数を表示
puts "Total Pokemon: #{Pokemon.total_pokemons}"
# 出力: Total Pokemon: 2

ポケモンを複数作成し、それぞれの情報を表示すると同時に、クラスメソッドを使用して総ポケモン数を取得できています。このように、クラスメソッドを使うことでクラス全体に関連する処理を実装することができます。
まとめとしてクラスメソッドと変数について重要性などを記載しました。

クラスメソッドとクラス変数の重要性

オブジェクト指向プログラミングにおいて、クラスメソッドとクラス変数は特別な役割を果す。クラス全体に関連する操作やデータを管理するために、便利な機能を提供することができます。

クラスメソッド

クラスメソッドは特定のインスタンスに紐づかないメソッドであり、クラス自体に関連づけられています。これにより、クラス内で共通の処理や操作を実行することができます。クラスメソッドはメソッド名の前に self. をつけることで定義されます。
使いどころ:

  • クラス全体に影響を与える操作を実装する場合
  • インスタンスを生成するファクトリーメソッドを提供する場合
  • クラス内のデータを処理するメソッドを作成する場合

クラス変数

クラス変数はクラス全体で共有される変数であり、全てのインスタンスが同じ値を共有します。これにより、クラス内でのデータの管理が容易になります。クラス変数は @@ で始まります。
使いどころ:

  • クラス全体で共有されるデータや設定の保存
  • インスタンス間で共通の値を保持する場合
  • クラスに関連する情報(例: 総インスタンス数)を管理する場合

ポケモンの例

ポケモンのクラスで使うと、全ポケモンで共通の情報(例: 総ポケモン数)を簡単に管理できます

# クラスメソッドを使ってポケモンを生成
pokemon_names = ['ピカチュウ', 'ヒトカゲ', 'ゼニガメ']
pokemons = Pokemon.create_pokemons(pokemon_names)
# 生成したポケモンの情報を表示
pokemons.each do |pokemon|
  pokemon.show_info
end
# クラス変数を使って総ポケモン数を表示
puts "Total Pokemons: #{Pokemon.total_pokemons}"

create_pokemons メソッドはクラスメソッドで、ランダムなレベルでポケモンを生成します。また、total_pokemons メソッドはクラス変数を利用して、総ポケモン数を取得します。
このように、クラスメソッドとクラス変数は、クラス全体で共有される情報や操作を具現化するのに役立ちます。

クラスの継承

railsを扱う上で、クラスの継承は重要な概念になっていると感じています。
継承を理解することで全体像の把握がしやすくなりました。
継承はクラスをベースとして新しいクラスを作成する仕組みです。既存のクラス(スーパークラスまたは親クラス)の特性や機能を引き継ぎつつ、それに新しい特性や機能を追加することができます。
またまた、ポケモンの例を作ってみました。基本のポケモンクラス(Pokemon)があり、このクラスにはポケモン共通の機能や性質が定義されています。このクラスを継承して、特定の種類のポケモンクラスを作成することができます。

class Pokemon
  attr_accessor :name, :level
  def initialize(name, level)
    @name = name
    @level = level
  end
  def show_info
    puts "Name: #{@name}, Level: #{@level}"
  end
  def use_attack(attack_name)
    puts "#{@name} used #{attack_name}!"
  end
end

上記のPokemonクラスを継承して、さらに特定のタイプのポケモンクラスを作成します。

class WaterTypePokemon < Pokemon
  def use_water_attack
    puts "#{@name}! みずてっぽう!"
  end
end

ここでWaterTypePokemonクラスはPokemonクラスを継承しています。これにより、WaterTypePokemonクラスはPokemonクラスのすべてのメソッドや属性を利用できます。また、新しくuse_water_attackメソッドを追加しています。これにより、水タイプのポケモンに特有の攻撃を使用できるようになります。

squirtle = WaterTypePokemon.new('ゼニガメ', 12)
squirtle.show_info
# 出力: Name: ゼニガメ, Level: 12
squirtle.use_attack('Tackle')
# 出力: ゼニガメ の タックル !
squirtle.use_water_attack
# 出力: ゼニガメ の みずてっぽう !

このようにして、クラスの継承を用いることで、共通の機能を持ちつつも特定のクラスに固有の機能を追加できます。
ちなみに組み込みライブラリでは
クラス階層を視覚的に表現すると、こんな感じです。

BasicObject
   ^
   |
 Object
   ^
   |
  Kernel (Module)
   ^
   |
  +-- String
  |
  +-- Numeric --+-- Integer
  |              |
  |              +-- Float
  |
  +-- Array
  |
  +-- Hash
  |
  ... (その他のクラス)

この図では、矢印(^)が継承を表し、BasicObjectが最上位に位置しています。ObjectクラスはBasicObjectを継承し、ほとんどの組み込みクラスはObjectクラスから派生。Kernelはモジュールであり、Objectにインクルードされているから、Objectクラスを通じて多くのメソッドが提供されています。
モジュールについては、次回の記事でまとめてみます。
String、Array、Hashなどのクラスは、すべてObjectクラスから直接または間接的に派生しており、Objectクラスのサブクラスとなっています。BasicObjectはObjectクラスのスーパークラスで、最も基本的な振る舞いのみを持っており、Objectクラスがより多くのメソッドや機能を提供している。
RubyプログラミングではBasicObjectとObjectの違いを意識することは少ないようなのですが、Rubyのオブジェクトモデルを深く理解するには重要なポイントになると思います。
ちなみに、以下のメソッドを使うことで、オブジェクトのクラスを確認できます。

オブジェクトのクラスを確認するメソッド一覧

  1. classメソッド
    • オブジェクトのクラスを返すメソッド。
    • 例: object.class
    • 使用例:
      pikachu.class
      # Output: Pokemon
      
  2. instance_of?メソッド
    • オブジェクトが指定したクラスのインスタンスであるかを判定するメソッド。
    • 例: object.instance_of?(Class)
    • 使用例:
      pikachu.instance_of?(Pokemon)
      # Output: true
      pikachu.instance_of?(String)
      # Output: false
      
  3. is_a?メソッド (または kind_of?メソッド)
    • オブジェクトが指定したクラスまたはその親クラスのインスタンスであるかを判定するメソッド。
    • 例: object.is_a?(Class)
    • 使用例:
      pikachu.is_a?(Pokemon)
      # Output: true
      pikachu.is_a?(Object)
      # Output: true
      pikachu.is_a?(String)
      # Output: false
      

クラスの継承を理解することで、オブジェクトがどのクラスから派生しているかを把握することができるので、これにより、組み込みライブラリや親クラスが提供する便利なメソッドや機能を調べて使うことがスムーズにできるようになりました。
この継承を理解した上で、リファレンスマニュアルで組み込みライブラリのクラスメソッドを色々調べてみると、自分の引き出しが増えていくと思います。

最後に

読んでいただきありがとうございました!
少しでも、クラス オブジェクト指向の理解に寄与することができたら幸いです。

参考文献

プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで. Software Design plusシリーズ. 伊藤淳一 著
https://gihyo.jp/book/2021/978-4-297-12437-3
参照年月(2023.5),p540~p629

37
20
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
20