LoginSignup
4
3

More than 5 years have passed since last update.

Rubyのクラスについての復習

Last updated at Posted at 2015-10-31

前回Rubyのクラスについての理解がふんわりとしていたので、改めて勉強しました。

はじめてのRuby
メタプログラミング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のようです。

4
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3