Monday, September 29, 2008

RubyのClassクラスはすべてのクラスの母であり、Moduleクラスはすべてのクラスの父である

class Module
  def m
    'm'
  end
end

Module.new.m # => "m"
Enumerable.m # => "m"
Math.m # => "m"

Kernel.m # => "m"

Class.new.m # => "m"

Object.m # => "m"
Array.m # => "m"
class MyClass
end
MyClass.m # => "m"

Module.m # => "m"

Class.m # => "m"
Moduleクラスはモジュールの生成クラスである
だからModuleクラスにinstanceメソッドmを定義すると
すべてのモジュールで定義されたモジュールメソッドself.mになる

KernelモジュールもModuleクラスで生成されるから
当然そのモジュールメソッドself.mになる

一方ModuleクラスはClassクラスのスーパークラスである
だからModuleクラスに定義されたinstanceメソッドmは
Classクラスで定義されたinstanceメソッドmになる

Classクラスはすべてのクラスの生成クラスだから
Classクラスのinstanceメソッドmは
すべてのクラスのクラスメソッドself.mになる

その中にはModuleクラスも含まれるから
Moduleクラスのクラスメソッドself.mにもなる

ここでModuleクラスはClassクラスのスーパークラスだから
Moduleクラスのクラスメソッドself.mは
Classクラスのクラスメソッドself.mになる

こうしてModuleクラスに定義されたinstanceメソッドmは
すべてのクラスにおけるクラスメソッドself.mになる

ここでKernelモジュールはObjectクラスにincludeされているので
そこに定義されたinstanceメソッドはObjectクラスに定義されたメソッドとなる
Objectクラスはすべてのクラスのスーパークラスだから
結果Kernelモジュールに定義されたinstanceメソッドは
すべてのクラスに定義されたinstanceメソッドとなる

ところがModuleクラスからKernelモジュールに渡された先のメソッドmは
Kernelモジュールにおいてモジュールメソッドself.mになるから
Objectクラスには渡されない

Objectクラス(およびKernelモジュール)に定義されたinstanceメソッドoは
すべてのクラスのクラスメソッド(モジュールメソッドを含む)となると共に
すべてのクラスのinstanceメソッドとなる
これはクラスから生成されるすべてのオブジェクトで
instanceメソッドoが使えることを意味している

一方でModuleクラスに定義されたinstanceメソッドmは
すべてのクラスのクラスメソッド(モジュールメソッドを含む)となるが
それらのinstanceメソッドになることはない

これらのことから
すべてのオブジェクトの挙動に影響を与えたいときは
Objectクラスにinstanceメソッドを定義すれば良く
すべてのクラスの挙動に影響を与えたいときは
Moduleクラスにinstanceメソッドを定義すればいい


No comments: