これもメモです。
2014/7/30追記(まとめ)
includeとextendの違いについて。
「オブジェクト指向スクリプト言語 Ruby」pp.195-196 から引用
モジュールを Mix-in としてクラスに「混ぜ込む」ためには include を使います。 include は Class クラスのメソッドで、あるモジュールで定義されているメソッドや定数を対象のクラスやモジュールに混ぜ込みます。正確には、そのクラスとスーパークラスの間にそのモジュールと同じ性質を持つ隠れたクラスを挟み込んだのと同じような働きをします。
補足すると、モジュールのメソッドがインスタンスメソッドとして働くということですね。
メソッドに対して特異メソッド(引用者註:クラスメソッド)があるように、クラスに対する include に対して、オブジェクト(引用者註:インスタンス)に対してモジュールの属性を追加する extend があります。 extend はモジュールに定義されているメソッドを特異メソッドとしてそのオブジェクトに追加する働きがあります。
extend は以下のような用途で使われます。
- オブジェクト(引用者註:インスタンス)にある性質をまとめて付加する
- クラスにクラスメソッドを追加する
こちらは(最後に記載されていますが)クラスメソッドとして追加するわけです。
言い直すと、クラスはClassクラスのインスタンスである、というRubyの言語としての性質から「オブジェクトに対してモジュールの属性を追加する extend」を使ってクラスにクラスメソッドを追加できるわけです。
いずれにせよモジュールのメソッドを(includeで)インスタンスメソッドへ組み込む、または(extendで)クラスメソッドなど特異メソッドへ組み込むわけで、モジュールのモジュールメソッド(特異メソッド)は組み込まれないということになります。
(追記ここまで)
もとの記事
moduleの取扱についてハッキリした気がしたので、http://memo.yomukaku.net/entries/319から引用させて貰いました*1。
mix-inする対象のmodule
method_1とクラスメソッド*2class_method_1を持っている# module1.rb # coding:utf-8 module Module1 def self.class_method_1 puts 'output form class_method_1' end def method_1 puts 'output from method_1' end end
引用者2014/7/30追記:
Module1モジュールのメソッドと特異メソッド(モジュールメソッド)を確認
Module1.instance_methods #=> [:method_1] Module1.singleton_methods #=> [:class_method_1]
includeを使った方法
includeを使うと、モジュールのメソッドがincludeしたクラスのインスタンスメソッドとして使える
# coding:utf-8 require './module1' class Sample include Module1 # moduleのクラスメソッドが使える Module1.class_method_1 # output form class_method_1 # こんなことはできない begin Module1.method_1 rescue Exception => e p e # #<NoMethodError: undefined method `method_1' for Module1:Module> end end # Sampleクラスのインスタンスを作る sample = Sample.new # moduleのメソッドがSampleクラスのインスタンスメソッドとして使用できる。 sample.method_1 # output from method_1
引用者2014/7/30追記:
module1モジュールをincludeされたSampleクラスのインスタンスメソッドと特異メソッド(クラスメソッド)を確認
Sample.instance_methods - Object.instance_methods => [:method_1] Sample.singleton_methods => []
モジュールのクラスメソッド*3(Module1.class_method_1)は、上記のように名前空間を指定して使えるが、includeしたクラスのクラスメソッドとしては使えない*4。
Sample.method_1 #=> NoMethodError: undefined method `method_1' for Sample:Class Sample.class_method_1 #=> NoMethodError: undefined method `class_method_1' for Sample:Class
extendを使った方法
extendを使うと、モジュールのインスタンスメソッド*5がクラスのクラスメソッドとして使える
# coding:utf-8 require './module1' class Sample extend Module1 # moduleのクラスメソッドが使える Module1.class_method_1 # output form class_method_1 # こんなことはできない begin Module1.method_1 rescue Exception => e p e # <NoMethodError: undefined method `method_1' for Module1:Module> end # extendするとmoduleのメソッドが自クラスのクラスメソッドとして使える self.method_1 # output from method_1 end # Sample.class_method_1はできない begin Sample.class_method_1 rescue Exception => e p e # #<NoMethodError: undefined method `class_method_1' for Sample:Class> end # Sampleクラスは先のようにmoduleのメソッドを自分のクラスメソッドとして持っている Sample.method_1 # output from method_1 # Sampleクラスのインスタンスを作る sample = Sample.new # moduleのメソッドはSampleクラスのインスタンスメソッドではない begin sample.method_1 rescue Exception => e p e # <NoMethodError: undefined method `method_1' for #<Sample:0x0000010085a0b0>> end
さらにincludeでもextendでも
モジュールのクラスメソッドはmix-inしたクラスのクラスメソッドとして使える。(2014/7/30削除)
引用者2014/7/30追記:
module1モジュールをextendされたSampleクラスのインスタンスメソッドと特異メソッド(クラスメソッド)を確認
Sample.instance_methods - Object.instance_methods #=> [] Sample.singleton_methods #=> [:method_1]
るりま
instance method Object#extend
extend(*modules) -> self
引数で指定したモジュールのインスタンスメソッドを self の特異 メソッドとして追加します。
Module#include は、クラス(のインスタンス)に機能を追加します が、extend は、ある特定のオブジェクトだけにモジュールの機能を追加 したいときに使用します。module Foo def a 'ok Foo' end end module Bar def b 'ok Bar' end end obj = Object.new obj.extend Foo, Bar p obj.a #=> "ok Foo" p obj.b #=> "ok Bar" class Klass include Foo extend Bar end p Klass.new.a #=> "ok Foo" p Klass.b #=> "ok Bar"extend の機能は、「特異クラスに対する Module#include」 と言い替えることもできます。
# obj.extend Foo, Bar とほぼ同じ class << obj include Foo, Bar end
トップレベルというかmainでのinclude
mainという暗黙のObjectクラスのインスタンスがselfである。トップレベルで何らかのメソッドを呼び出すと、self.method、つまりmainオブジェクトのメソッドを呼び出す形になる。
:
def self.hogeで定義したメソッドは全てのクラスのクラスメソッドになり、def hogeで定義したメソッドは全てのインスタンスメソッドになる。
:
class文の中のincludeはModuleクラスのクラスメソッドであるところのincludeだけど、トップレベルのincludeは微妙に特殊で、トップレベルのincludeメソッドつまりmainオブジェクトのincludeメソッドを実行するとObjectクラスのincludeクラスメソッドを呼んだような挙動をする。
:
mainオブジェクトはObjectクラスのインスタンスなのでincludeしたモジュールの中でdef fugaとして定義されていたメソッドがインスタンスメソッドとして使える、そのためfugaが組み込み関数のように見える
requireとincludeとextendとmodule_function(1) : As Sloth As Possible
この記事(連作)は他の内容も良いので再読予定。
requireとincludeとextendとmodule_function(1) : As Sloth As Possible
requireとincludeとextendとmodule_function(2) : As Sloth As Possible