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
と階層構造が形成される。