include とextendについて調べていた時にこの記事の中でincludeを2回していた。
その時に2回includeするとメソッド探索の階層構造で上下関係はどうなるんだろうと思って調べて見た。
サンプルコード
def puts_safe
puts yield
rescue => e
puts "Error :< - #{e}"
end
class Neko
class << self
def class_method
"class method in Neko"
end
end
def instance_method
"instance method in Neko"
end
end
module Fur
def m_method_for_color(color = "white")
"module method: my fur is #{color} color."
end
def m_method_for_length(length = 2)
"module method: my fur is #{length} cm."
end
end
module Claw #つめ
def m_method_for_color(color = "white")
"module method: my claw is #{color} color."
end
end
class Puma < Neko
include Fur
include Claw # ここを入れ替えたらどうなるのか気になった。
def a_instance_method
puts "a instance method in Puma"
end
end
puts "<<pum1>>"
pum1 = Puma.new
puts_safe { pum1.m_method_for_color } #=>module method: my claw is white color.
puts_safe { pum1.m_method_for_length } #=>module method: my fur is 2 cm.
この例だと
Puma < Claw < Fur < Neko < Object < Kernel < BascObjectとなっていることがわかった。
実際にPuma.new.m_method_for_colorと````module Claw```にもmodule Farにも書かれているメソッドを実行すると`module Claw`の方が実行された。
入れ替えてみる
includeの部分を入れ替えて見た。
def puts_safe
puts yield
rescue => e
puts "Error :< - #{e}"
end
class Neko
class << self
def class_method
"class method in Neko"
end
end
def instance_method
"instance method in Neko"
end
end
module Fur
def m_method_for_color(color = "white")
"module method: my fur is #{color} color."
end
def m_method_for_length(length = 2)
"module method: my fur is #{length} cm."
end
end
module Claw #つめ
def m_method_for_color(color = "white")
"module method: my claw is #{color} color."
end
end
class Puma < Neko
+ include Claw
+ include Fur
def a_instance_method
puts "a instance method in Puma"
end
end
puts "<<pum1>>"
pum1 = Puma.new
puts_safe { pum1.m_method_for_color } #=>module method: my fur is white color.
puts_safe { pum1.m_method_for_length } #=>module method: my fur is 2 cm.
前回の例では
Puma < Claw < Fur < Neko < Object < Kernel < BascObject
となっていたものが
今回の例では
Puma < Fur < Claw < Neko < Object < Kernel < BascObject
となっていた。
結論
includeすると愚直にクラスの階層1個上にmoduleを追加する。
class ClassC < classD
include moduleA
include moduleB
end
と記述があるとき、もちろん上から実行されるので
include moduleAが実行された時点では
class C < moduleA < classD < Object < Kernel < BasicObjectとなる。
次に下の行のinclude moduleBが実行されて
class C < moduleB < moduleA < classD < Object < Kernel < BasicObject
と階層構造が形成される。