【Ruby】Mix-inは継承なのか
Mix-inは継承なのか
RubyのMix-inの挙動は継承とよく似ています。
superメソッドを使って確認してみましょう。
module Greetable def say_hello p "Hello Greetable" end end class Human include Greetable def say_hello p "Hello Human" super end end Human.new.say_hello #Hello Human #Hello Greetable
Humanクラスのsay_helloメソッドでsuperを使って、オーバーライドされている親クラスのsay_helloメソッドを呼び出しています。
ここで"Hello Greetable"が表示されたので、Moduleを親クラスと認識していることがわかります。
別の角度から見てみましょう。
Moduleにインスタンス変数を宣言してinitializeで値を設定するようにします。
module Greetable @name def initialize @name = "hoge" end end class Human include Greetable def say_hello p "Hello #{@name}" end end Human.new.say_hello #Hello hoge
正常に宣言され、エラーが出ることもなく値も取得できました。
つまり、Moduleのインスタンスが生成されたことになります。
しかしModuleにはnewメソッドが定義されていないため、インスタンスは生成できません。
実際にはModuleを元に生成された無名クラスを継承することでMix-inを実現しています。
ひし形継承問題はあるか
Mix-inが無名クラスの継承だとすると、ひし形継承した場合はどうなるのでしょうか。
module Grandpa @grandpa_name = "" end module Papa include Grandpa def teach_me_papa_grandpa @grandpa_name = "Taro" end end module Mama include Grandpa def teach_me_mama_grandpa @grandpa_name = "Jiro" end end class Me include Papa include Mama def say_grandpa_name p "My grandpa is #{@grandpa_name}" end end me = Me.new me.teach_me_papa_granpa me.teach_me_mama_granpa me.say_grandpa_name # Jiro
MeクラスはPapaクラスとMamaクラスを継承し、PapaクラスとMamaクラスはそれぞれGrandPaクラスを継承しています。
Meクラスのインスタンスが作成される際、父方の祖父と母方の祖父の2つの祖父インスタンスが作成される必要があります。
しかし、インスタンスを2つ作成すると、MeクラスからGrandpaクラスのメンバにアクセスした際にどちらのインスタンスにアクセスすればいいのかわからなくなってしまいます。
逆にインスタンスを1つだけ生成した場合、父も母も1つのインスタンスにアクセスしてしまい、2人の祖父を表現できません。
これがひし形継承問題です。
上記コードでは同じインスタンスにアクセスして値が書き換えられてしまっており、インスタンスを一つしか作っていないことがわかります。
つまり、Mix-inでもひし形継承問題は解決してるとは言えません。
ModuleをClassの代わりに利用するのは危険です。
本来の使い方から逸脱しないようにしましょう。