Rubyはオブジェクト指向言語であるのだけれど、Javaの継承と考え方、方法が違うようなので、メタプログラミング Rubyを読みつつ調べてみた。
Module
RubyにはModuleという概念があり、これはメソッドがまとめられたものである。通常の継承のほかにこのModuleをincludeやprependすることにより、メソッドをクラスに取り込むことができるもよう。
早速試してみる。
通常の継承の例
includeの例
通常の継承の例
[1] pry(main)> class MySubClass
[1] pry(main)* def subClass
[1] pry(main)* p "sub class"
[1] pry(main)* end
[1] pry(main)* end
=> :subClass
[2] pry(main)> class MyClass < MySubClass
[2] pry(main)* def myClass
[2] pry(main)* p "my class"
[2] pry(main)* end
[2] pry(main)* end
=> :myClass
[3] pry(main)> myclass = MyClass.new
=> #<MyClass:0x0000000d1eb628>
[4] pry(main)> myclass.subClass
"sub class"
=> "sub class"
ふむ、普通にメソッドを継承してる。includeの例
[2] pry(main)> module MyModule
[2] pry(main)* def myModuleMethod
[2] pry(main)* p "myModuleMethod"
[2] pry(main)* end
[2] pry(main)* end
=> :myModuleMethod
[3] pry(main)> class MyClass
[3] pry(main)* include MyModule
[3] pry(main)* def myMethod
[3] pry(main)* p "myMethod"
[3] pry(main)* end
[3] pry(main)* end
=> :myMethod
[4] pry(main)> myClass = MyClass.new
=> #<MyClass:0x0000000bf719e8>
[5] pry(main)> myClass.myModuleMethod
"myModuleMethod"
=> "myModuleMethod"
むむ、こちらも普通にメソッドを継承してる。
prependの例
[3] pry(main)> module MyModule
[3] pry(main)* def myModuleMethod
[3] pry(main)* p "myModuleMethod"
[3] pry(main)* end
[3] pry(main)* end
=> :myModuleMethod
[4] pry(main)> class MyClass
[4] pry(main)* prepend MyModule
[4] pry(main)* def myClass
[4] pry(main)* p "my class"
[4] pry(main)* end
[4] pry(main)* end
=> :myClass
[5] pry(main)> myclass = MyClass.new
=> #<MyClass:0x0000000cd80e08>
[6] pry(main)> myclass.myModuleMethod
"myModuleMethod"
=> "myModuleMethod"
はい、こちらも普通に継承
extendの例
じゃあinclude と pretend は一体何が違うのかというと、メソッドを呼び出していく順番に違いが出てくるらしい。
Includeの例 クラスの親階層にはいる。
SubMyClassから呼び出した時に、PretendだとMyModuleが先にメソッドがあるかどうか走査され、IncludeだとMyClassが先に走査される。
extendの例
[1] pry(main)> module MyModule
[1] pry(main)* def myModuleMethod
[1] pry(main)* p "myModuleMethod"
[1] pry(main)* end
[1] pry(main)* end
=> :myModuleMethod
[2] pry(main)> class MyClass
[2] pry(main)* extend MyModule
[2] pry(main)* def myClass
[2] pry(main)* p "my class"
[2] pry(main)* end
[2] pry(main)* end
=> :myClass
[3] pry(main)> myclass = MyClass.new
=> #<MyClass:0x0000000d374c88>
[4] pry(main)> myclass.myModuleMethod
NoMethodError: undefined method `myModuleMethod' for #<MyClass:0x0000000d374c88>
from (pry):13:in `<main>'
[5] pry(main)> MyClass.myModuleMethod
"myModuleMethod"
=> "myModuleMethod"
おお、extendはクラスメソッドになった!じゃあinclude と pretend は一体何が違うのかというと、メソッドを呼び出していく順番に違いが出てくるらしい。
Includeの例 クラスの親階層にはいる。
2.1.5 :001 > module MyModule
2.1.5 :002?> def my_class
2.1.5 :003?> p "my_class in my module"
2.1.5 :004?> end
2.1.5 :005?> end
=> :my_class
2.1.5 :006 > class MyClass
2.1.5 :007?> include MyModule
2.1.5 :008?> def my_class
2.1.5 :009?> p "my class"
2.1.5 :010?> end
2.1.5 :011?> end
=> :my_class
2.1.5 :012 > class SubMyClass < MyClass; end
=> nil
2.1.5 :013 > SubMyClass.ancestors
=> [SubMyClass, MyClass, MyModule, Object, Kernel, BasicObject]
2.1.5 :019 > sub_my_class = SubMyClass.newPretendの例 クラスの子階層の子になる。
=> #<SubMyClass:0x000000024ecff8>
2.1.5 :020 > sub_my_class.my_class
"my class"
=> "my class"
2.1.5 :001 > module MyModule
2.1.5 :002?> def my_class
2.1.5 :003?> p "my_class in my module"
2.1.5 :004?> end
2.1.5 :005?> end
=> :my_class
2.1.5 :006 > class MyClass
2.1.5 :007?> prepend MyModule
2.1.5 :008?> def my_class
2.1.5 :009?> p "my class"
2.1.5 :010?> end
2.1.5 :011?> end
=> :my_class
2.1.5 :012 > class SubMyClass < MyClass; end
=> nil
2.1.5 :013 > SubMyClass.ancestors
=> [SubMyClass, MyModule, MyClass, Object, Kernel, BasicObject]
2.1.5 :016 > sub_my_class = SubMyClass.new
=> #<SubMyClass:0x000000011fabc0>
2.1.5 :017 > sub_my_class.my_class
"my_class in my module"
=> "my_class in my module"
SubMyClassから呼び出した時に、PretendだとMyModuleが先にメソッドがあるかどうか走査され、IncludeだとMyClassが先に走査される。