はじめに
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 クラスに定義されているので、それがメッセージに対する応答として実行されています。
具体的な流れ
-
pikachu
オブジェクトがattack
メソッドを呼び出します。 -
attack
メソッドが Pikachu クラス内に定義されているため、それが実行されます。 - 実行された結果、"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
属性を持ち、初期化時に設定された値を取得できます。
具体的な流れ
-
Pokemon
クラスの定義でattr_accessor :name
が宣言されているため、name
属性の読み書きが可能になります。 -
initialize
メソッドで、Pokemon
クラスのインスタンス(この場合はpikachu
オブジェクト)が生成され、初期化時に引数として渡された名前が@name
属性にセットされます。 -
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
これにより、外部からname
やlevel
メソッドを使ってインスタンス変数にアクセスできます。
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
によって、name
とlevel
のゲッターとセッターが自動的に生成されます。
例として、クラスメソッドも加えてポケモンの例を作成してみます。クラスメソッドはクラス自体に関連する処理を行うメソッドで、クラスメソッドではインスタンス変数にアクセスできない特徴があります。
- ポケモンクラスを拡張したコード例
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のオブジェクトモデルを深く理解するには重要なポイントになると思います。
ちなみに、以下のメソッドを使うことで、オブジェクトのクラスを確認できます。
オブジェクトのクラスを確認するメソッド一覧
-
class
メソッド- オブジェクトのクラスを返すメソッド。
- 例:
object.class
- 使用例:
pikachu.class # Output: Pokemon
-
instance_of?
メソッド- オブジェクトが指定したクラスのインスタンスであるかを判定するメソッド。
- 例:
object.instance_of?(Class)
- 使用例:
pikachu.instance_of?(Pokemon) # Output: true pikachu.instance_of?(String) # Output: false
-
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