はじめに
こんにちは、progateでRubyを勉強したのち「ゼロからわかるRuby超入門」を勉強しています。
その中でもスーパークラスの要素を子クラスに引き継ぐ継承と、module
の両方を勉強すると、「module
なくても継承だけでよくない?」と思うようになったのでその違いや使い分けについて本日は解説していきたいと思います
継承とは
継承は、あるクラスが他のクラスの属性やメソッドを「引き継ぐ」仕組みです。
サブクラス (子クラス) がスーパークラス (親クラス) のすべてのメソッドや定数を継承し、必要に応じてオーバーライドすることができます。
継承の特徴は以下の通りです
- Rubyでは1つのクラスしか継承できない
- クラス間に明確な親子関係があり、サブクラスはスーパークラスのすべての機能を継承する
- サブクラスで親クラスのメソッドを上書きできる(オーバーライド)
- スーパークラスのメソッドや定数が引き継がれる
※ここで、注意点としてインスタンス変数は自動で引き継がれません。スーパークラスのインスタンス変数を引き継ぐためにはsuper
メソッドを使用する必要があります
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
を使用することで、本来はスーパークラスから継承されていないインスタンス変数を子クラスに引き継ぐことができます
では、継承はどのような場合に使うのでしょうか
- 自然な親子関係があるとき
たとえば、「犬」や「猫」などの動物が「動物」という概念に含まれるとき。 - 属性を共有する必要があるとき
親クラスが持っているデータや振る舞いをサブクラスが「必ず」持っているとき
継承という名の通り、同じようなものである必要があるのでパンとスマホとか全く関係ない要素同士の場合は継承をしない方がよいということです。
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
を使ってインスタンス変数を引き継ぐようにふるまわせることも可能です
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 Swimmable
def swim
"泳いでいます"
end
end
class Dog
include Swimmable
end
class Human
include Swimmable
end
ここでは、Dog と Human が 「泳ぐ」という共通の機能 を持っていますが、「犬は人間である」という関係はありません。
そこで、モジュールを使って swim
メソッドを共有しています。
クラス同士に親子関係は必要なく、ただ機能を共有したいときにモジュールを使います。
まとめ
いかがだったでしょうか。ややこしいですが、属性の違いについて着目するということが重要であると思います。
上記の内容を簡単にまとめておきます
継承を使うべきケース
- クラス間に明確な親子関係があるとき(「犬は動物である」など)
- サブクラスが親クラスの基本的な機能を拡張したりオーバーライドする必要があるとき
module
を使うべきケース
- 親子関係がない複数のクラスに共通の機能を提供したいとき(「犬は人間ではないが、どちらも泳ぐことができる」など)。
- 複数の機能を自由に追加したいとき(モジュールは複数同時に include できる)
インスタンス変数の引継ぎについては継承とmodule
どちらもできるのでそれがどちらかを使う理由にはならなそうです。
最後に本記事はコメントにより一部修正しました。
独学であるため、このようなコメントはとても励みになります。
これからも記事投稿していくので何かあればコメントいただけると幸いです。