[ruby]Ruby中的Singleton class

219 阅读4分钟

Singleton class

是类,又名metaclass或者 eigenclass。是谈到Ruby object model时绕不开的话题。

方法调度

要充分理解Singleton class,必须要弄清楚Ruby中的方法调度(method dispatching)。看例子:

class Vehicle
  def initialize(kms)
    @kms = kms
  end
  def drive
    puts "let's go!"
  end
end
  
car = Vehicle.new(20_000)  
car.drive

上述代码定义了Vehicle类,并创建了实例car,对car调用了方法drive。下图展示了内存中对Vehicle类和car实例的表示。

内存中,实例car是通过C struct来表示的,包含了实例变量的列表。同时,实例对象car还包含了一个kclass指针,指向实例化该对象的类-Vehicle。Vehicle类中包含了实例方法列表及指针super,该指针指向了Vehicle类的superclass - Object (默认情况下,所有类的父类)。

实际上实例对象car并不知道自己有个drive方法,它会通过表示它的C struct中的kclass指针找到它的类,然后在类(Vehicle)的实例方法列表中找到这个drive方法,并真对实例对象car调用这个方法。

由于 "Everything in Ruby is object",类也是对象,针对类调用方法的时候,也会遵循如上的策略,下面的例子在上面代码的基础上,加入了类变量和类方法:

class Vehicle  
  @@registry = []

  def self.register(vehicle)
    @@registry << vehicle
  end
  # ...
end

car = Vehicle.new(20_000)  
Vehicle.register(car)

上述代码例子中,定义了类变量 - @@registry(2个@开头的变量是类变量),以及一个类方法register。内存中的对象(包括Vehicle类)表示如下:

不过,类方法register在哪里?Vehicle类中只包含了实例方法列表,不会包含类方法的列表。但是,不管是实例对象,还是类对象,进行方法调度的机制应该是一样的。这意味着,Vehicle类也必须通过kclass指针到它的类中去找到这个register方法。但是,我们不能把这个类方法register方法到Class类中,否则,所有的类都会有个register方法。

Ruby就是靠Singleton class类这个问题。

Singleton class登场

Ruby会为实例对象/类对象创建一个Singleton class。这个class只是只为这个特定的对象创建的,并对我们是隐藏的。当针对某个对象调用方法的时候,首先到这个对象的Singleton class中去查找(如果有Singleton class的话),找到就会调用。

(图中#A表示类对象A的Singleton class,#a表示实例对象a的Singleton class)

上图实际进行了适当简化,文章末尾会给出完整的object model。

当通过self.register定义类方法的时候,实际上是在告知Ruby为Vehicle类创建它的Singleton class,并在其中添加这“实例方法”register。

如何告知Ruby为实例对象创建Singleton class呢?例子如下:

car = Vehicle.new(20_000)
def car.start # Ruby会为实例对象car创建Singleton class,并将方法start放在其中
    puts "starting..."
end

上述代码会为实例对象car创建Singleton class - #car。

不过,当对实例对象car调用class方法的时候,返回是类Vehicle,并不是car的Singleton class(记得上面有提过,Singleton class对我们是隐藏的)。不过,由于#car的super 指向了Vehicle类,因此,我们仍然可以通过super来改变Vehicle中的方法行为,达到覆写的目的:

car = Vehicle.new(1000)  
bus = Vehicle.new(3000)

def car.drive  
  print "I'm driving a car! "
  super
end

car.drive # "I'm driving a car! let's go!"  
bus.drive # "let's go!"

关于类对象的Singleton class

我们编写个稍微复杂点的例子:

class Vehicle  
  @@registry = []
  def self.register(vehicle)
    @@registry << vehicle
  end
end

class Car < Vehicle  
  def self.register(car)
    # register car plate
    super
  end
end

some_car = Car.new  
Car.register(some_car)

上述程序中对象的内存表示:

注意图中#Car的super指针是指向#Vehicle的,这种关系,让Car中的类方法register调用(super)父类中的类方法成为了可能。

#Object的super是指向Class的。所有的Ruby类继承自Object,它们对象的Singleton class继承自#Object,最终继承自Class。

至此,我们能从Ruby的object model中总结出2个规则:

  1. 实例对象的Singleton class的superclass是这个对象的类。
  2. 类对象的Singleton class的superclass是这个类的superclass的Singleton class。

Reference

Diving into Ruby Singleton Classes