前回Rubyのクラスについての理解がふんわりとしていたので、改めて勉強しました。
includeとextend
インクルードとは、指定されたモジュールの定義 (メソッド、定数) を引き継ぐことです。
instance method Module#include
module Foo
def foo
end
end
class Bar
end
bar = Bar.new
p bar.respond_to?(:foo) # => false
class Bar
include Foo
end
p bar.respond_to?(:foo) # => true
includeはメソッド定義を引き継ぐ、とあるので、moduleのなかにあるメソッドをそのままincludeしたクラスのなかに展開するイメージだとわかりやすいと思います。(でも、あくまでも理解しやすい為のイメージです)
class Bar
include Foo
end
# ↓こんな感じです。
#class Bar
# def foo
# end
#end
じゃあ、クラスメソッドをincludeでクラスに持たせたい時は?
module Foo
def self.foo_foo
end
end
class Bar
include Foo
end
# ↓こんな感じを期待している
#class Bar
# def self.foo_foo
# end
#end
p Bar.respond_to?(:foo_foo) # => false
ここは期待通りの結果にはならないみたいです。では、クラスメソッドとして定義されたメソッドはどこに行ったのかというと、moduleであるFooのクラスメソッドになっています。
module Foo
def self.foo_foo
end
end
class Bar
include Foo
end
p Bar.respond_to?(:foo_foo) # => false
p Foo.respond_to?(:foo_foo) # => true
includeはmoduleのインスタンスメソッドをincludeしたクラスに引き継ぐ、もののようです。
moduleのメソッドをincludeで、対象クラスのクラスメソッドとして引き継がせたい時に登場するのが特異クラスです。
module Foo
# インスタンスメソッドとして定義しておく
def foo_foo
end
end
class Bar #Barクラスをオープンして
class << self # Barクラスの特異クラスをオープンしてそこにinclude
include Foo
end
end
p Bar.respond_to?(:foo_foo) # => true
Barクラスの特異クラスをオープンして、特異クラスのインスタンスメソッド (Barのクラスメソッド)として追加する。
急に出てきた特異クラスというものは、クラスメソッドが定義されている場所です。
class Bar
# ここにBarクラスのインスタンスメソッドが定義される
def bar
end
class << self # 特異クラスをオープン
# ここにBarクラスのクラスメソッドが定義される
def bar_bar
end
end
end
クラスメソッドの分かりにくさは構文の定義方法が3つある、というのも理由の一つかと思います。
追記
特異メソッドの定義方法は、実質的には2つではないかというご指摘をいただきました。
class Bar
# 特異クラスにメソッドを定義するクラスメソッドの構文
# ↓ def (オブジェクトを表す式).(メソッド名) の形式
def self.bar_bar
end
def Bar.bar_bar
end
# ↓ 特異クラスの中で定義
class << self
def bar_bar
end
end
#結果的に同じことです。
end
このクラスの特異クラスにインスタンスメソッド(クラスのクラスメソッド)を追加することを、クラス拡張というそうです。
この特異クラスというものはすべてのオブジェクトにあるそうです。
class Bar
end
bar = Bar.new
p bar.respond_to?(:bar) # => false
class << bar # barオブジェクトの特異クラスをオープン
# berオブジェクトの特異クラスに特異メソッドを定義
def bar
end
end
p bar.respond_to?(:bar) # => true
bar_bar = Bar.new # 同じクラスの別のインスタンスオブジェクト
p bar_bar.respond_to?(:bar) # => false
これはオブジェクト拡張と呼ばれるそうです。
クラスの場合もオブジェクトの場合も特異クラスの開き方とメソッドの定義の仕方は同じみたいです。
上記の例で言えば、クラス名Bar
はクラスオブジェクトを参照する定数オブジェクトで、参照先はクラスオブジェクト(Classクラスのインスタンスオブジェクト)。変数bar
はクラスのインスタンスオブジェクトです。ややこしいですが、どちらもオブジェクトなので、特異クラスをもっているということです。
特異メソッドとクラスメソッドは同じ物を指す言葉です。
クラス拡張に対してはクラスメソッドと言い、オブジェクト拡張に対しては特異メソッド、と言い分けているのかな?と思います。
次はextendです。
と言ってもextendが何をする物なのかはもう出ています。
引数で指定したモジュールのインスタンスメソッドを self の特異 メソッドとして追加します。
instance method Object#extend
module Foo
def foo
end
end
class Bar
end
bar = Bar.new
p bar.respond_to?(:foo) # => false
bar.extend Foo # 特異クラスにメソッドを追加。
p bar.respond_to?(:foo) # => true
bar_bar = Bar.new
p bar_bar.respond_to?(:foo) # => false
module Foo
def foo
end
end
class Bar
extend Foo # 特異クラスにメソッドを追加
end
p Bar.respond_to?(:foo) # => true
特異クラスをオープンしてincludeをする例を見た後なら、理解しやすい動きだと思います。
特異クラスを開かずに特異クラスにメソッドを追加するメソッドがextendのようです。