Ruby の include, prepend, extend の違いと用途についてまとめました。
Module#include
指定したモジュールをinclude
することをMix-in
といい、クラスがモジュールをインクルードすると、モジュールのインスタンスメソッドを手に入れます。クラスをインクルードすることはできませんがモジュールにインクルードすることはできます。
module MyModule def my_method; "#{__method__} called!!"; end end class MyClass include MyModule end MyClass.new.my_method #=> "my_method called!!" MyClass.instance_methods.grep(/my_method/) #=> [:my_method] MyClass.my_method #=> NoMethodError: undefined method `my_method' for MyClass:Class
インクルードされたモジュールにクラスメソッドが存在しても、インクルードしたクラスからは呼び出せません。
module MyModule def self.my_method; "#{__method__} called!!"; end end class MyClass include MyModule end MyClass.my_method #=> NoMethodError: undefined method `my_method' for MyClass:Class MyClass.methods.grep(/my_method/) #=> []
モジュールをインクルードすると、継承チェーンはインクルードするクラスの真上に入ります。メソッドの探索はスーパークラスよりもインクルードされたモジュールのほうが先に行われます。
MyClass.ancestors => [MyClass, MyModule, Object, Kernel, BasicObject]
クラス拡張
module
をinclude
してクラスメソッドを定義するには、特異クラスにモジュールをインクルードします。インクルードしたモジュールは特異クラスのインスタンスメソッドになり、MyClass のクラスメソッドとなります。
module MyModule def my_method; "#{__method__} called!!"; end end class MyClass class << self include MyModule end end MyClass.my_method #=> "my_method called!!" MyClass.singleton_methods #=> [:my_method]
モジュール拡張
クラス拡張をオブジェクトに適用することをモジュール拡張と呼びます。オブジェクトの特異クラスにモジュールをインクルードします。
module MyModule def my_method; "#{__method__} called!!"; end end obj = Object.new class << obj include MyModule end obj.my_method #=> "my_method called!!" obj.singleton_methods #=> [:my_method]
instance method Module#include (Ruby 2.4.0)
Module#prepend
指定したモジュールをprepend
すると、include
と同様にモジュールのインスタンスメソッドを手に入れます。継承チェーンはプリペンドしたクラスの手前に入ります。そのため、結果としてプリペンドしたクラスで定義されているメソッドはオーバーライドされます。self のメソッドを使用したい場合はsuper
で呼び出すことが可能です。オープンクラスで String の reverse メソッドをオーバーライドしてみました。
module MyModule def reverse self.size > 5 ? self.upcase : super end end class String prepend MyModule end "programing".reverse #=> "PROGRAMING" "ruby".reverse #=> "ybur"
継承チェーンは以下のの通りです。
String.ancestors => [MyModule, String, Comparable, Object, Kernel, BasicObject]
instance method Module#prepend (Ruby 2.4.0)
Object#extend
指定したモジュールをextend
すると、モジュールのインスタンスメソッドを特異メソッドとして追加します。include
はクラスのインスタンスにメソッドを追加しますが、extend
はレシーバの特異クラスにモジュールをインクルードしてクラスメソッドとして使用できます。つまり、特異クラスに対するinclude
であるクラス拡張 (モジュール拡張) と同じです。
module MyModule def my_method; "#{__method__} called!!"; end end class MyClass extend MyModule end MyClass.new.my_method #=> NoMethodError: undefined method `my_method' for #<MyClass:0x007fd32104f280> MyClass.singleton_methods #=> [:my_method] MyClass.my_method #=> "my_method called!!"
obj = Object.new obj.extend MyModule obj.my_method #=> "my_method called!!" obj.singleton_methods #=> [:my_method]
include
と同様に extend されたモジュールにクラスメソッドが存在しても、extend したクラスからは呼び出せません。
module MyModule def self.my_method; "#{__method__} called!!"; end end class MyClass extend MyModule end MyClass.my_method #=> NoMethodError: undefined method `my_method' for MyClass:Class MyClass.methods.grep(/my_method/) #=> []