どう違うのかいまいち分からなかったので、触りながら確認していきます。
サンプル
module Piyo
def piyo_piyo
end
end
class Foo
end
class Bar <Foo
end
class Hoge < Bar
end
↓を見ながらいじっていきます。
hoge = Hoge.new
p "Hoge.ancestors => #{Hoge.ancestors}"
p "Foo.respond_to?(:piyo_piyo) => #{Foo.respond_to?(:piyo_piyo)}"
p "Bar.respond_to?(:piyo_piyo) => #{Bar.respond_to?(:piyo_piyo)}"
p "Hoge.respond_to?(:piyo_piyo) => #{Hoge.respond_to?(:piyo_piyo)}"
p "hoge.respond_to? => #{hoge.respond_to?(:piyo_piyo)}"
実行結果
"Hoge.ancestors => [Hoge, Bar, Foo, Object, Kernel, BasicObject]"
"Foo.respond_to?(:piyo_piyo) => false"
"Bar.respond_to?(:piyo_piyo) => false"
"Hoge.respond_to?(:piyo_piyo) => false"
"hoge.respond_to?(:piyo_piyo) => false"
各クラスと一番継承先のHogeクラスのインスタンスhogeにincludeとextendでどう変化するのか見ていきます。
FooにPiyoをinclude
module Piyo
def piyo_piyo
end
end
class Foo
include(Piyo)
end
class Bar <Foo
end
class Hoge < Bar
end
hoge = Hoge.new
p "Hoge.ancestors => #{Hoge.ancestors}"
p "Foo.respond_to?(:piyo_piyo) => #{Foo.respond_to?(:piyo_piyo)}"
p "Bar.respond_to?(:piyo_piyo) => #{Bar.respond_to?(:piyo_piyo)}"
p "Hoge.respond_to?(:piyo_piyo) => #{Hoge.respond_to?(:piyo_piyo)}"
p "hoge.respond_to? => #{hoge.respond_to?(:piyo_piyo)}"
実行結果
"Hoge.ancestors => [Hoge, Bar, Foo, Piyo, Object, Kernel, BasicObject]"
"Foo.respond_to?(:piyo_piyo) => false"
"Bar.respond_to?(:piyo_piyo) => false"
"Hoge.respond_to?(:piyo_piyo) => false"
"hoge.respond_to?(:piyo_piyo) => true"
ancestorsにPiyoが追加されて、Hogeクラスのインスタンスhogeでmoduleのメソッドが使えるようになりました。
includeを差し込んだクラスの一つ上にmoduleが階層に追加されるようです。
extendを見てみます。
FooにPiyoをextend
module Piyo
def piyo_piyo
end
end
class Foo
extend(Piyo)
end
class Bar <Foo
end
class Hoge < Bar
end
hoge = Hoge.new
p "Hoge.ancestors => #{Hoge.ancestors}"
p "Foo.respond_to?(:piyo_piyo) => #{Foo.respond_to?(:piyo_piyo)}"
p "Bar.respond_to?(:piyo_piyo) => #{Bar.respond_to?(:piyo_piyo)}"
p "Hoge.respond_to?(:piyo_piyo) => #{Hoge.respond_to?(:piyo_piyo)}"
p "hoge.respond_to?(:piyo_piyo) => #{hoge.respond_to?(:piyo_piyo)}"
実行結果
"Hoge.ancestors => [Hoge, Bar, Foo, Object, Kernel, BasicObject]"
"Foo.respond_to?(:piyo_piyo) => true"
"Bar.respond_to?(:piyo_piyo) => true"
"Hoge.respond_to?(:piyo_piyo) => true"
"hoge.respond_to?(:piyo_piyo) => false"
ancestorsにPiyoが表示されなくなりました。Hogeクラスのインスタンスhogeもmoduleのメソッドを使えなくなったようです。
ここがよくわからなかったのですが、「extendはあるオブジェクトに特異メソッドを付与する」ものみたいです。
特異クラスはクラスメソッドの様なもので継承はする。クラスメソッドの様なものなので、インスタンスには影響しない。(クラスの中でextendするとクラスメソッドの様に振る舞う?)
という理解で大丈夫なら表示結果と矛盾もないので納得できます。
extend (Object)
特異メソッド定義
instance method Module#include
インスタンスhogeにextend
module Piyo
def piyo_piyo
end
end
class Foo
end
class Bar <Foo
end
class Hoge < Bar
end
hoge = Hoge.new
p "Hoge.ancestors => #{Hoge.ancestors}"
p "Foo.respond_to?(:piyo_piyo) => #{Foo.respond_to?(:piyo_piyo)}"
p "Bar.respond_to?(:piyo_piyo) => #{Bar.respond_to?(:piyo_piyo)}"
p "Hoge.respond_to?(:piyo_piyo) => #{Hoge.respond_to?(:piyo_piyo)}"
p "hoge.respond_to?(:piyo_piyo) => #{hoge.respond_to?(:piyo_piyo)}"
hoge.extend(Piyo)
p "hoge.respond_to?(:piyo_piyo) => #{hoge.respond_to?(:piyo_piyo)}"
実行結果
"Hoge.ancestors => [Hoge, Bar, Foo, Object, Kernel, BasicObject]"
"Foo.respond_to?(:piyo_piyo) => false"
"Bar.respond_to?(:piyo_piyo) => false"
"Hoge.respond_to?(:piyo_piyo) => false"
"hoge.respond_to?(:piyo_piyo) => false"
↓extendしたインスタンスだけ使える
"hoge.respond_to?(:piyo_piyo) => true"
extendは対象がオブジェクトなので、インスタンスに直接使える様です。
extendする場所によって、クラスメソッドの様にもインスタンスメソッドの様にもなるところがややこしいです。
includeでも以下で同様にできるようです。
インスタンスhogeにinclude
class << hoge
include(Piyo)
end