【Ruby】非常に強力なRubyのクラス構成

Rubyのクラス構成は非常にシンプルで強力です。
クラス構成を理解することがそのままRubyを理解することにつながります。

Classクラス

RubyにはClassクラスというものが定義されています。
Rubyの組み込みクラスは全てClassクラスのインスタンスになります。
例えばObjectクラスやStringクラスなどがClassクラスのインスタンスです。

"Hello".class   #String
String.class    #Class
Object.class    #Class

Rubyでは全てのクラスがnewメソッドを持っていますが、もちろん全てのクラスでnewメソッドを定義しているわけではありません。
Classクラスに定義して、それぞれのクラスで使用しています 。
Classクラスのメソッドを見ると、先程挙げたnewメソッドなど、クラスで共通して使われるメソッドが定義されていることがわかります。

Class.methods
#[:nesting, :constants, :allocate, :new, :superclass 他省略

以下に図でClassとインスタンスの関係を示します。

f:id:taiki_sano:20160923225921p:plain

特異メソッドとクラスメソッド

ところで、Rubyには特異メソッドというものがありました。
これはインスタンスに特有の振舞いを追加するものです。

class Test
end

a = Test.new
b = Test.new

#特異メソッド定義
def a.say_hello
  puts "Hello"
end

a.say_hello # Hello
b.say_hello # NoMethodError

全てのクラスはClassクラスのインスタンスなので、クラスにも同様にして特異メソッドを定義できます。

class Test
end

#特異メソッド定義
def Test.say_bye
  puts "Bye"
end

Test.say_bye #bye
String.say_bye #NoMethodError

クラスの特異メソッドとクラスメソッドは同じものであることに気がつくと思います。 クラスメソッドの定義方法を再度確認してみましょう。

class Test
    #クラスメソッド定義
  def self.say_bye
    puts "Bye"
  end
end

Test.say_bye #bye
String.say_bye #NoMethodError

インスタンス名がselfに変わっただけで、特異メソッドの定義方法と同様であることがわかります。

特異メソッドの定義場所

インスタンスがメソッドを呼び出すにはクラスに定義する必要があります。
では特異メソッドはどこに定義されているのでしょうか。
Rubyではインスタンス生成時に特異クラスというそのインスタンス専用のクラスを生成し、継承させています。
想像しづらいと思いますので図解します。

f:id:taiki_sano:20160924202438p:plain
"Hello"という文字列はStringクラスのインスタンスですが、インスタンス生成時に"Hello"インスタンス専用のクラスが生成されます。
このクラスはStringクラスを継承しています。
これを"Hello"インスタンスの特異クラスといい、特異メソッドを定義するとこのクラスにインスタンスメソッドが定義されます。
特異クラスはsingleton_classというメソッドで確認できます。
特異クラスに定義されているメソッドを見てみましょう。

class Test
end

c = Test.new

#特異メソッド定義
def c.say_hoge
  puts "hoge"
end

c.singleton_class.instance_methods false
#[:say_hoge]

特異メソッドが特異クラスに定義されていることがわかります。
特異クラスはインスタンスのクラスと継承関係にあるので同名のメソッドはオーバーライドされます。

class Test
  def say_your_name
    puts "Test"
  end
end

d = Test.new

def d.say_your_name
  puts "d"
end

d.say_your_name #d

Mix-inもそうでしたが、Rubyの処理はほとんどが継承関係、またはクラスとインスタンスの関係で説明がつきます。
このシンプルな構成がRubyの魅力の一つです。