【Ruby】Moduleをincludeするとはどういうことか
RubyではModuleを読み込むのに複数の方法があります。
include、prepend、extendなどが挙げられます。
何気なく使っていませんか?
とんでもない落とし穴があるかもしれません。
実際に内部でどういった処理が行われているのか、それぞれの違いを確認してみましょう。
include
おそらくModuleを勉強した時に一番最初に見るのがincludeかと思います。
Moduleをクラスにincludeすると、クラスにModuleで定義したメソッドが追加されます。
これはModuleをもとにして作成された無名クラスを、includeしたクラスの親として継承することで実現しています。
module M1 end class C1 include M1 end C1.ancestors #[C1, M1, Object, Kernel, BasicObject]
C1の親にM1が設定されているのがわかると思います。
そのため、M1で定義されたメソッドはC1で定義されたメソッドでオーバーライドされます。
module M1 def greet puts "hello" end end class C1 include M1 def greet puts "hi" super end end C1.new.greet #hi #hello
superメソッドはオーバーライドされたメソッドを呼び出します。
後から"hello"が表示されたことでM1のgreetメソッドがオーバーライドされていることがわかります。
ただし、Module内で定義された特異メソッドは継承されないので注意が必要です。
module M1 def self.lough puts "hahaha" end end class C1 include M1 end M1.lough #hahaha C1.lough #NoMethodError
特異メソッドはM1ではなく、M1の特異クラスに定義されているため、無名クラスに定義が反映されないためです。
prepend
prependはincludeとよく似ていますが、継承の順番が変わります。
module M2 end class C2 prepend M2 end C2.ancestors #[M2, C2, Object, Kernel, BasicObject]
M2の方が子になっているのがわかると思います。
そのため、includeとは逆で、C2で定義されたメソッドはM2で定義されたメソッドでオーバーライドされます。
module M2 def greet puts "hello" super end end class C2 prepend M2 def greet puts "hi" end end C2.new.greet #hello #hi
prependはモジュールの処理を優先したい場合に使います。
extend
includeとprependはモジュールを継承関係に組み込んでいたのに対し、extendはクラスとインスタンスの関係に組み込みます。
module M3 def greet puts "hello" end end class C3 extend M3 def greet puts "hi" end end C3.greet #hello C3.new.greet #hi C3.ancestors #[C3,Object,Kernel,BasicObject]
継承関係にM3が存在しないことがわかります。
ところで、先ほどのgreetをクラスメソッドにしたらどうなるでしょうか。
module M4 def greet puts "hello" end end class C4 extend M4 def self.greet "hi" end end C4.greet #hi
クラスで定義したメソッドでOverrideされました。
クラスメソッドは特異クラスに定義されますので、それがOverrideされたということはその特異クラスの親クラスとしてモジュールが組み込まれているということがわかります。
特異クラスの継承関係を見てみましょう。
C4.singleton_class.ancestors #[#<Class:C4>, M4, #<Class:Object>, Class, Module, Object, Kernel, BasicObject]
※#<Class:インスタンス名>はそのインスタンスの特異クラスを表します。
C4で定義したクラスメソッドは#<Class:C4>のインスタンスメソッドとして定義されるため、M4で定義されているgreetをOverrideしたという形になっています。
include、prepend、extendを理解することで、意図しないOverrideをしないように気をつけましょう。