0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby】継承とmoduleの使い分け

Last updated at Posted at 2024-10-17

はじめに

こんにちは、progateでRubyを勉強したのち「ゼロからわかるRuby超入門」を勉強しています。
その中でもスーパークラスの要素を子クラスに引き継ぐ継承と、moduleの両方を勉強すると、「moduleなくても継承だけでよくない?」と思うようになったのでその違いや使い分けについて本日は解説していきたいと思います

継承とは

継承は、あるクラスが他のクラスの属性やメソッドを「引き継ぐ」仕組みです。
サブクラス (子クラス) がスーパークラス (親クラス) のすべてのメソッドや定数を継承し、必要に応じてオーバーライドすることができます。
継承の特徴は以下の通りです

  • Rubyでは1つのクラスしか継承できない
  • クラス間に明確な親子関係があり、サブクラスはスーパークラスのすべての機能を継承する
  • サブクラスで親クラスのメソッドを上書きできる(オーバーライド)
  • スーパークラスのメソッドや定数が引き継がれる
    ※ここで、注意点としてインスタンス変数は自動で引き継がれません。スーパークラスのインスタンス変数を引き継ぐためにはsuperメソッドを使用する必要があります
instance_variable.rb
class Animal
  def initialize
    @name = "Animal"
  end

  def name
    @name
  end
end

class Dog < Animal
  def initialize
    super  # Animalクラスのinitializeを呼び出す
    @breed = "Bulldog"
  end

  def breed
    @breed
  end
end

dog = Dog.new
puts dog.name  # => "Animal"
puts dog.breed  # => "Bulldog"

superを使用することで、本来はスーパークラスから継承されていないインスタンス変数を子クラスに引き継ぐことができます

では、継承はどのような場合に使うのでしょうか

  • 自然な親子関係があるとき
    たとえば、「犬」や「猫」などの動物が「動物」という概念に含まれるとき。
  • 属性を共有する必要があるとき
    親クラスが持っているデータや振る舞いをサブクラスが「必ず」持っているとき

継承という名の通り、同じようなものである必要があるのでパンとスマホとか全く関係ない要素同士の場合は継承をしない方がよいということです。

inheritance.rb
class Animal
  def speak
    "動物の鳴き声"
  end
end

class Dog < Animal
  def speak
    "ワンワン"
  end
end

class Cat < Animal
  def speak
    "ニャーニャー"
  end
end

この例では、Animal クラスは speak という共通のメソッドを持っており、それを Dog と Cat というサブクラスが「それぞれの鳴き声」に合わせてオーバーライドしています。
「犬は動物である」という明確な親子関係があるので、継承は理にかなっています。

moduleとは

moduleは、機能 (メソッド) の「共有」を目的としています。複数のクラスで同じ機能を使いたい場合に、継承ではなくmoduleをMix-inすることで、その機能を「借りる」形で利用できます。
これも上にならって特徴を見てみましょう。

  • 複数のmoduleを1つのクラスに取り込むことができる
  • クラスの階層構造を作らない(親子関係がない)
  • 複数のクラスに共通の機能を提供するのに適している
  • moduleはメソッドのみを提供し、インスタンス変数を引き継がない
    ※基本的にはインスタンス変数を引き継がないのですが、moduleを使ってインスタンス変数を引き継ぐようにふるまわせることも可能です
instance_valuable.rb
module Animal
  def initialize(name)
    @name = name
  end
end

class Dog
  include Animal
end

dog = Dog.new("Rex")
p dog  # => #<Dog:0x000000012d03bff0 @name="Rex">

dogを確認すると@nameというインスタンス変数を持っている(引き継いでいる)ことがわかります。
なので、性質としてはインスタンス変数を引き継ぎませんが、操作によって引き継がせることは可能ということです。

ではmoduleを使う場面についても見ていきましょう

  • 複数のクラスに共通のメソッドを追加したいとき
    そのメソッドはクラス間で共通していても、クラス同士の親子関係が必要ない場合。
  • 機能を柔軟に追加したいとき
    継承は1回だけしかできませんが、モジュールなら複数の機能を柔軟に追加できます。

親子関係が必要なく、複数のクラスで使いたい場合に使えそうだなということがここからの記述でわかりますよね。

module.rb
module Swimmable
  def swim
    "泳いでいます"
  end
end

class Dog
  include Swimmable
end

class Human
  include Swimmable
end

ここでは、Dog と Human が 「泳ぐ」という共通の機能 を持っていますが、「犬は人間である」という関係はありません。
そこで、モジュールを使って swim メソッドを共有しています。
クラス同士に親子関係は必要なく、ただ機能を共有したいときにモジュールを使います。

まとめ

いかがだったでしょうか。ややこしいですが、属性の違いについて着目するということが重要であると思います。
上記の内容を簡単にまとめておきます

継承を使うべきケース

  • クラス間に明確な親子関係があるとき(「犬は動物である」など)
  • サブクラスが親クラスの基本的な機能を拡張したりオーバーライドする必要があるとき

moduleを使うべきケース

  • 親子関係がない複数のクラスに共通の機能を提供したいとき(「犬は人間ではないが、どちらも泳ぐことができる」など)。
  • 複数の機能を自由に追加したいとき(モジュールは複数同時に include できる)

インスタンス変数の引継ぎについては継承とmoduleどちらもできるのでそれがどちらかを使う理由にはならなそうです。

最後に本記事はコメントにより一部修正しました。
独学であるため、このようなコメントはとても励みになります。
これからも記事投稿していくので何かあればコメントいただけると幸いです。

0
0
2

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?