【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の代わりに利用するのは危険です。
本来の使い方から逸脱しないようにしましょう。