用 Ruby 尤其是 Ruby On Rails 来做一些简单有用的东西并不需要你理解 Ruby 语言的所有东西,Ruby 语言的设计是优美而符合惯例的,在有其它语言的基础上很容易对照官网的教程快速的达到“使用”级别。
我用了总共一个星期的业余时间,每天下班的两三个小时,从头学 Ruby 和 Ruby On Rails,并重写了这个博客程序。 但这显然不应该是专业程序员应该有的态度,而且在用 Ruby 重写博客的时候,越来越被 Ruby 吸引了。 于是仔细的重新读了一遍 Ruby 的经典书籍《The Ruby Programming Language》并试图理解有关 Ruby 语言的所有东西。这是一本非常好的书,很清晰深入地解释了 Ruby 语言的方方面面,推荐所有想要深入学习 Ruby 的人都仔细的读一遍。
It is easy to program in Ruby, but Ruby is not a simple language.
这句话很好的概括了 Ruby 语言的学习曲线。但 Ruby 的设计贯穿着概念上的一致性,这点对理解 Ruby 中那“条条大路通罗马”中的所有大路以及相对其它语言稍显复杂的 Class, Module 以及 Class 继承关系有很大的帮助。这篇博客主要想要比较清楚深入的阐述 Ruby 的 method lookup path ,顺便看一下 Ruby 设计上的概念一致性的优雅以及对理解 Ruby 语言的帮助。
Method Lookup Path
假设我们有一个 Cat 类:
class Cat
include Miaowable
include Runable
end
然后有一只猫 garfield = Cat.new, 假设我们想要调用 garfield.miaow,Ruby 的 method loopup 或者叫 name resolution 过程如下:
- 检查
garfield的特征类对象(eigenclass),有则调用,没有者继续往下找。 下面类似。
- 检查
garfield.class对象的(也就是 Cat) 的 instance methods
- 依次检查
Runable和Miaowable模块的 instance methods
- 重复2和3两个步骤从下往上依次检查继承链上的所有类已及该类所包含模块的 instance methods, 直到 BasicObject 类
- 如果都没有找到 miaow 这个方法,那么重复上面的步骤,查找
method_missing方法,执行找到的第一个方法。Object 类所包含的模块 Kernel定义了这个方法,所以这次查找一定可以成功。
这里是针对一个实例的查找过程, 关于 method lookup path 的一般性步骤请参考 《The Ruby Programming Language》。
什么是特征类(eigenclass)?
eigenclass 也被称为 singleton class 或者 metaclass 。 你应该可以猜出来了,简单来说它是定义对象的 singleton 方法的地方。
这是一个匿名类,用 class << o 打开一个对象的 eigenclass. 还记得当需要定义多个 class method 的时候 Ruby 提供了一个像下面这种简单的方式吗:
class Something
class << self
def class_method1
end
def class_method2
end
end
end
这是因为 Something 类的 class method 实际上就是 Something 对象(不是 Something 的 instance,是 Something 本身就是个对象)的 singleton 方法。 在上面代码中 self 出现在 Something 的定义体内而不是在某个方法的定义中,这个
时候 self 就是 Something 对象本身,所以 class << self 就打开了 Something 对象的 eigenclass, 里面定义的方法就是 Something 对象的 singleton 方法,也就是 class method 。
回到我们的 garfield 对象, 现在为这个对象定义一个 singleton 方法 s_m :
class << garfield
def s_m
end
end
或者
def garfield.s_m
end
为了更好的理解 eigenclass 类,我们为 Cat 类定义一个实例方法用来返回类实例对象的 eigenclass 对象:
class Cat
def eigenclass
class << self
self
end
end
end
下面我们来看看 eigenclass 的有意思的东西:
> garfield.class
=> Cat
> garfield.eigenclass.class
=> Class
> garfield.eigenclass.superclass
=> Cat
> garfield.eigenclass.instance_methods(false) #don't get inherited methods
=> [:s_m] #我们为 garfield 定义的 singleton 方法
所以,一个对象的 eigenclass 就像是一个隐形的类,位于该对象的继承链的第一层,该对象所定义的所有 singleton 方法位于其中,其结构如下图:

什么是instance methods
Ruby 的 instance methods 没什么太特殊的地方,就像你在其它OOP语言中所理解的那样,在类中定义,在类的实例上调用。
Eigenclass 以及 Class Method 的继承关系
如果按照上面的方法来定位一个 Class Method 会出现一些令人迷惑的事情。 现在我们先为 Cat 定义一个 class method :
class Cat
class << self
def we_are_not
"tigers"
end
end
end
并且增加一个子类 TabbyCat :
class TabbyCat < Cat
class << self
def we_have
"distinctive coats"
end
end
end
> TabbyCat.we_have
=> "distinctive coats"
> TabbyCat.we_are_not
=> "tigers"
由于 Class 本身也是对象, 那么 Ruby 在查找 class method 的时候理论上也应该跟查找普通实例的方法没什么差别。
TabbyCat 是 Class 类的实例, Class 继承自 Module,Module 继承自 Object ..., 那么 we_are_not 的查找路径应该是:
TabbyCat -> eigenclass of TabbyCat -> Class -> Module -> Object -> BasicObject
你会发现在这个链上并没有 we_are_not 方法的定义。 那么是 Ruby 在处理 class method 的时候没有采用一个统一的方式么? 其实不是。
现在我们在 Object 类上同样定义一个方法来返回实例的 eigenclass ,这样我们就能够拿到 Cat 和 TabbyCat 的 eigenclass :
class Object
def eigenclass
class << self
self
end
end
end
然后:
> TabbyCat.eigenclass
=> #<Class:TabbyCat>
> Cat.eigenclass
=> #<Class:Cat>
> TabbyCat.eigenclass.superclass
=> #<Class:Cat>
> TabbyCat.eigenclass.superclass == Cat.eigenclass
=> true
> Cat.eigenclass.superclass == Object.eigenclass
=> true
> Object.eigenclass.superclass
=> #<Class:BasicObject>
发现了吗? TabbyCat 的 eigenclass 是继承自 Cat 的 eigenclass 的。 所以在上面那个查找路径在查找到 eigenclass of TabbyCat 的时候就已经找到
了 we_are_not 。 也就是说如果一个类继承自另外一个类,那么它的 eigenclass 也继承自父类的 eigenclass 。所以当我们查找 TabbyClass.we_are_not 时候,
实际的查找链应该是如下图:
