Ruby 对象和类模型

用 Ruby 也快有一年了,总结一下 Ruby 中对象和类模型,表达不是很熟练,请轻喷。

Ruby的对象和类模型到底是什么鬼?

在 Ruby 的世界中,有这么三个原则:

  • 所有东西都是对象,除了 method 和 block

  • 所有对象都有一个类

  • 类最终都继承自源类 Object,而 Object 继承自 BasicObject

在这个描述中,假设定义了一个类 Dog,实例化一个对象 tidy,对象 tidy 的类是 Dog,Dog 类的类是 Class,
Class 继承自 Module,Module 继承自 Object,Object 最终继承自源类 BasicObject,BasicObject 也是对象,那 BasicObject 的继承自谁?

在这里我打开 irb,ruby 的版本为2.1.2,进行操作。

class Dog ; end
tidy = Dog.new
tidy.class #=> Dog
Dog.class  #=> Class
Class.superclass #=> Module
Module.superclass #=> Object
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil

很明显,BasicObject 没有超类,这条贪食蛇吃到这里就终止了。
而Dog继承关系上的类概念:Dog,Class,Module,Object,BasicObject 的类都是 Class。

也就是说我可以创建这些类概念的实例对象,Dog 就是一只狗,Class 就是一个类,Module 就是一个模块,
Object 就是一个对象,BasicObject 就是一个基础对象。而 Dog,Module 和 Class 自己本身都是 Class 类的一个
实例对象,可以去创建我们想要用的概念对象。

而 Object 和 BasicObject 包括一些对象的基本方法,例如 Object 的 nil? 方法,
因为所有对象都会继承自 Object,所以所有对象都会有 nil 方法;又例如 BasicObject 的 instance_eval 方法,因为所有对象 也会继承自 BasicObject,所以所有对象也会有 instance_eval 方法。
这些方法都可以在 Ruby的手册上看到。

这里我画了一个继承的图出来。

那 BasicObject 和 Object 有没有 nil? 和 instance_ev 方法呢?
当然会有,因为他是 Class 的实例对象,对象都继承 Object 和 BasicObject,
他们都可以享有自己的方法。

而我们在讨论对象和类模型的时候一般都会忽略掉 BasicObject,因为他置于最顶层,我们不会轻易改变他们,
而只讨论 Object 以下的对象和类。

eigenclass

eigenclass 也叫元类或者单件类。eigen 的意思为本质的,换我们中国人古代的概念应该叫元神。
每个对象都有自己的 eigenclass,可以通过连个方法找到他。下面代码找出了一只泰迪狗和 Dog 类的元类。

def Dog ; end
tidy = Dog.new
tidy_eigenclass = class << tidy
   self
end 
tidy_eigenclass => #<Class:#<Dog:0x000000023bc5e8>>
tidy_eigenclass.class #=> Class
tidy_eigenclass.superclass #=> Dog

Dog_eigenclass = class << Dog
    self
end
Dog_eigenclass=> #<Class:Dog>
a = Dog_eigenclass.class=> Class
Dog_eigenclass.superclass=> #<Class:Object>
Dog_eigenclass == a #=> true

上面的代码的意思是:在泰迪狗和 Dog 类的之前还有一个 eigenclass,Dog 类与 Class 类之前还有一个 eigenclass。
其实可以通过 class << XXXXXX.singleton_class 两种方法来找到对象的元类:

Dog_eigenclass = class << Dog
    self
end
Dog_eigenclass=> #<Class:Dog>
Dog.singleton_class=> #<Class:Dog>

那元类有什么作用呢?
其实有了元类,当我们想要扩展对象和类自身的方法而非继承方法的时候,就变得非常容易,而且可以使用很多种方法来实现。

class Dog
  def self.bar
    "wowowo"
  end
end
class Dog
  class << self
    def bar
      "wowowo"
    end
  end
end
def Dog.bar
  "wowowo"
end
class << Dog
  def bar
    "wowowo"
  end
end

上面的定义代码使得 Dog 类都可以用 Dog.bar 来输出”wowowo”。
对类来说,可以使用 class_eval 来打开自己来操作类自己。
对对象和类来说,可以使用 instance_eval来打开自己的 eigenclass 来操作自己。
其实自己再画一个继承图就很好理解了。

其实还有一个 nil 我还没有画,而 nil 的 class 是 NilClass。当定义一个 Dog,和实例一个对象 tidy 就成这样的图了。
这个只是局部的显示出 eigenclass 视图。有人做出了一个不包含 eigenclass 的 Ruby 常用类的关系图,真心跪了。

总结

想要精通 Ruby,完全熟悉 Ruby 的对象和类模型是不可缺失的一步。

参考