QA@IT

Rubyのincludeとextendについて

4497 PV

Rubyを使い始めて半年ほどの者です。

先日、Module を使うときに include ではなく extend を使う場合があることを初めて知りました。
WEBで include と extend の違いについて調べてみましたが、Module のメソッドをクラスのインスタンスメソッドのように使いたいならincludeを、クラスメソッドのように使いたいならextendを使う、という程度の表面的な理解しか出来ませんでした。

include や extend がどのような仕組みなのか疑問です。
extend については、クラスがClassクラスのインスタンスなので、モジュールのメソッドをclassクラスのインスタンスの特異メソッドとして定義するのだろうかと推測しました。
includeはクラスのインスタンスが生成されるときにモジュールのメソッドを混ぜ込む??? みたいな事を考えましたが、混乱してよくわからなくなりました。

どなたか include や extend の違いや仕組みについて教えていただけると嬉しいです。

回答

Module#include と Object#extend の違いは

  • Module#include は、レシーバーとなるモジュール or クラスに引数となる Module の機能をいれる
  • Object#extend は、レシーバーとなるオブジェクトの特異クラスに引数となる Module の機能をいれる

になります。

Module#include は、レシーバとなるクラスやモジュールの継承ツリー上でその親クラスの間に対象のモジュールをいれこみます。
Object#extend は、レシーバとなるクラスやオブジェクトの特異クラスの継承ツリー上でその親クラスの間に対象のモジュールをいれこみます。

クラスに対して include と extend したときの違いは 、そのクラスの ancestors とクラスの特異クラスの
ancestors を extend 前後で観察すると違いがわかると思います。

module Foo; end
module Bar; end
class Buzz; end

Buzz.ancestors #=> [Buzz, Object, Kernel, BasicObject] 
# 特異クラスの存在は処理系では存在してないように扱われるので見えないがClassの前にいます
Buzz.singleton_class.ancestors  #=> [Class, Module, Object, Kernel, BasicObject] 

class Buzz
   include Foo
   extend Bar
end

# include した Foo がでてくる
Buzz.ancestors #=> [Buzz, Foo, Object, Kernel, BasicObject] 
# extend した Bar がでてくる
Buzz.singleton_class.ancestors #=> [Bar, Class, Module, Object, Kernel, BasicObject] 

クラスメソッドはクラスオブジェクトの特異メソッドです。クラスメソッドが呼ばれたときのメソッド探索は特異クラスの継承ツリーを辿ることになり、そこで
extend したメソッドが見つかるため、クラスに対して extend することで、クラスメソッドを追加できているのです。

編集 履歴 (1)
  • 丁寧な説明をいただきありがとうございます。とてもわかりやすかったです。特異クラスなどについて調べつつ理解を深めてみたいと思います。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。