この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
今回は、includeとextendについて調べてみました。
include, extendとは
include,extendは、モジュールのメソッドをクラスに読み込む機能を持っています。異なる点は、includeがインスタンスメソッド、extendがクラスメソッドとしてクラスに読み込みます。コードでは、以下のようになります。
module Dog
def dog
'Bow wow'
end
end
class IncludeDog
include Dog
end
class ExtendDog
extend Dog
end
include_dog = IncludeDog.new
include_dog.cry
=> "Bow wow"
ExtendDog.cry
=> "Bow wow"
extendは、クラスのインスタンスに対して実行することもできます。この場合、実行したインスタンスにのみ、extendしたモジュールのメソッドが使用することができます。クラスのインスタンスに対してextendした場合は、特異メソッドとして取り込まれます。
module Dog
def cry
'Bow wow'
end
end
class Animal
end
animal = Animal.new
animal.extend Dog
animal.cry
=>"Bow wow"
includeにもextendと同様に、インスタンスを使用した方法があります。
animal = Animal.new
class << animal
include Dog
end
animal.cry
=>"Bow wow"
モジュール読み込みとオーバーライドについて
モジュールとクラスに同じメソッドが存在した場合、include, extendを行うことでどちらのメソッドがオーバーライドされるのか気になったので調べてみました。結果は、クラス内でinclude, extendした場合は、クラスで定義したメソッドが呼び出され、クラスのインスタンスを使用してinclude,extendした場合は、モジュールで定義したメソッドが呼び出されました。
クラス内でinclude, extendした場合に、クラスのメソッドを上書きしてモジュールのメソッドを呼び出す方法が無いか調べてみると、includeの代わりにprependを使用することで解決できることが分かりました。includeとprependの違いは、継承される順番が異なることです。includeの場合だと「クラス < モジュール」、prependだと「モジュール < クラス」となります。
module Dog
def cry
'module method'
end
end
class PrependDog
prepend Dog
def cry
'prepend dog'
end
end
prepend_dog = PrependDog.new
prepend_dog.cry
=> "module method"
# PrependDogの親クラス一覧 一番左が自分のクラス、右が親クラス
PrependDog.ancestors
=> [Dog, PrependDog, ..., Object, BasicObject]
まとめ
今回は、includeとextendについてまとめてみました。
- インスタンスメソッドとして取り込むなら
include
- クラスメソッドとして取り込むなら
extend
- インスタンスに対して
include, extend
を行うことでクラスのメソッドを上書きする(メソッド名が重複する場合) prepend
で、クラスのメソッドを上書きする(メソッド名が重複する場合)