Ruby プログラミング

[Ruby] オブジェクト指向プログラミング2(クラスの継承)

class2

こんにちは!じゃいごテックのあつしです。

オブジェクト指向プログラミング第2弾はクラスの継承についてご紹介します。
継承は共通の性質を持ったオブジェクトに対して使われます。例えば、犬、猫、兎というクラスを作るときに共通の性質を哺乳類などとしてスーパークラスで定義(汎化)して共通化し、それぞれの特徴はそれぞれのクラスで定義(特化)する手法です。

クラスの継承

クラスは、あるクラス(スーパークラス・親クラス)の機能を引き継いだクラス(サブクラス・子クラス)を定義することができます。
前回、Appleクラスを定義した時にnewメソッドでインスタンスを生成してclassメソッドでクラスを確認しましたが、Appleクラスの中身は空だったはずですよね。
実は自動的にObjectクラスを継承しているので、Objectクラスのクラスメソッドであるnewメソッドやインスタンスメソッドのclassメソッドが使えたというわけです。
同様に組込みライブラリのクラスでも継承が行われていて、良く使うクラスの継承関係は下図のようになっています。

 

標準ライブラリ_継承関係

出典:プロを目指す人のためのRuby入門

継承の例

# 空のAppleクラス
class Apple
end

# AppleクラスはObjectクラスのクラスメソッドnewメソッドが使える
ringo = Apple.new

# AppleクラスのインスタンスはObjectクラスのインスタンスメソッドclassメソッドが使える
p ringo.class
# > Apple

# AppleクラスのスーパークラスはObjectクラス
p ringo.class.superclass
# > Object

# ObjectクラスのスーパークラスはBasicObjectクラス
p ringo.class.superclass.superclass
# BasicObject

# 123 は Integerクラス
p 123.class
# > Integer

# IntegerクラスのスーパークラスはNumericクラス
p 123.class.superclass
# > Numeric

# NumericクラスのスーパークラスはObjectクラス
p 123.class.superclass.superclass
# > Object

クラスを継承してみる

ロールプレイングゲームを例に継承を実装してみます。
スーパークラスをVillager(村人)クラス、サブクラスをHero(勇者)クラスとします。

Villagerクラス

  • インスタンス変数
    • @name : String
    • @hp : Integer
  • インスタンスメソッド
    • greet : "こんにちは" と出力する
    • attack : "#{@name} は木の棒で殴りかかった" と出力する

先ずはVillagerクラスを継承したHeroクラスを作ってみます。

class Villager
  def initialize(name, hp)
    @name = name
    @hp = hp
  end

  def greet
    puts "こんにちは"
  end

  def attack
    puts "#{@name} は木の棒で殴りかかった"
  end
end

# HeroクラスはVillagerクラスを継承
class Hero < Villager
end

# Villagerクラスのインスタンスを生成
villager = Villager.new("村人A", 10)
p villager
# > #<Villager:0x00007fffdd0a8248 @name="村人A", @hp=10>
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった

# Heroクラスのインスタンスを生成
hero = Hero.new("よしひこ", 20)
p hero
# > #<Hero:0x00007fffdd0a75f0 @name="よしひこ", @hp=20>
hero.greet
# > こんにちは
hero.attack
# > よしひこ は木の棒で殴りかかった

HeroクラスVillagerクラスの特性を引き継いでいることが確認出来ました。

オーバーライド

スーパークラスで定義されたメソッドと同じ名前のメソッドをサブクラスで定義することによって、メソッドを上書きすることが出来ます。

勇者は木の棒ではなく、剣で攻撃したいので、Heroクラスattackメソッドをオーバーライドします。

class Villager
  def initialize(name, hp)
    @name = name
    @hp = hp
  end

  def greet
    puts "こんにちは"
  end

  def attack
    puts "#{@name} は木の棒で殴りかかった"
  end
end

class Hero < Villager
  # attackメソッドをオーバーライド
  def attack
    puts "#{@name} は光の剣で切りかかった"
  end
end

# Villagerクラス
villager = Villager.new("村人A", 10)
p villager
# > Villager
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった

# Heroクラス
hero = Hero.new("よしひこ", 20)
p hero
# > Hero
hero.greet
# > こんにちは
hero.attack
# > よしひこ は光の剣で切りかかった

同じattackメソッドですが、VillagerクラスHeroクラスで異なる処理を行うことが出来ました。
今回は実装していないですが、戦士だったら斧で攻撃とかになりますかね。このように同じメソッドでもクラスによって違う処理を行うことをポリモーフィズム(日本語だと多態性とか多様性)といいます。

super

super はオーバーライドしているスーパークラスのメソッドを呼び出します。カッコと引数が省略された場合は現在のメソッドの引数がそのまま渡されます。
引数を渡さずにオーバーライドしたメソッドを呼び出すにはsuper()と明示します。

initializeメソッドgreetメソッドsuperを使ってオーバーライドしてみます。また、Heroクラスインスタンスメソッドmagicを定義してみます。

Heroクラス

  • インスタンス変数
    • @name : String (Villagerクラスから継承)
    • @hp : Integer (Villagerクラスから継承)
    • @mp : Integer
  • インスタンスメソッド
    • greet :
      1行目に "こんにちは" と出力する (superでVillagerクラスから呼び出す)
      2行目に "私は勇者#{@name}です!" と出力する
    • attack : "#{@name} は光の剣で切りかかった" と出力する
    • magic : "#{@name} は魔法をとなえた" と出力する
class Villager
  def initialize(name, hp)
    @name = name
    @hp = hp
  end

  def greet
    puts "こんにちは"
  end

  def attack
    puts "#{@name} は木の棒で殴りかかった"
  end
end

class Hero < Villager
  # initializeメソッドをオーバーライド
  def initialize(name, hp, mp)
    # @name, @hp はVillagerクラスで初期化
    super(name, hp)
    # @mp はHeroクラスのインスタンス変数
    @mp = mp
  end

  # greetメソッドをオーバーライド
  def greet
    # Villagerクラスのgreetを呼び出す
    super
    puts "私は勇者#{@name}です!"
  end

  # attackメソッドをオーバーライド
  def attack
    puts "#{@name} は光の剣で切りかかった"
  end

  # Heroクラスのインスタンスメソッドを定義
  def magic
    puts "#{@name} は魔法をとなえた"
  end
end

# Villagerクラス
villager = Villager.new("村人A", 10)
p villager
# > #<Villager:0x00007fffc1197eb8 @name="村人A", @hp=10>
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった

# Heroクラス
hero = Hero.new("よしひこ", 20, 10)
p hero
# > #<Hero:0x00007fffc1197af8 @name="よしひこ", @hp=20, @mp=10>
hero.greet
# > こんにちは
# > 私は勇者よしひこです!
hero.attack
# > よしひこ は光の剣で切りかかった
hero.magic
# > よしひこ は魔法をとなえた

Villagerクラスの特性を再利用しつつ、Heroクラスを実装することが出来ました。

今回のまとめ

今回はクラスの継承についてご紹介しました。

  • 継承(スーパークラス・サブクラス)
  • オーバーライド(ポリモーフィズム)
  • super

継承とオーバーライドを使うと、似たような性質を持つクラスを効率よく定義出来ますね!

【PR】Ruby学習でお世話になった本




【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-Ruby, プログラミング
-, , , ,

© 2024 じゃいごテック