Ruby 单件类

来源:互联网 发布:淘宝网上代运营 编辑:程序博客网 时间:2024/06/03 18:53

  

这两种方法是完全一样的,所以谁先定义谁就被覆盖,谁后定义谁起作用

 

第一种方式:

 

这个self就是T1这个类对象,因此class << T1里面的内容就是T1的singleton类,而里面的内容就是T1这个对象的singleton方法

和普通对象的singleton方法不同,类的singlton类是一开始就存在的,它是干什么的,它用来存放只对这个类对象有意义的方法,什么方法是只对这个类对象有意义的方法,显然就是这个类的实例对象的类方法。类的singleton class就是meta-class


由于self是一个类对象,所以看起来有点复杂。但理解这个概念完全可以通过考虑一个普通的对象:

 

显然两种方法是一摸一样的


单件类(Singleton Class)

在Ruby中,定义一个类可以有两种方法:

这两种方法的存在是一个事实造成的,Ruby不但可以定义适合普通的类,还可以为某一个具体的对象定义它特定的类。譬如,我们定义了一个普通的类A.

现在你可以用A创建出很多对象,例如:

由于a1,a2,a3都是A的实例,因此它们的行为都是一样的。譬如,它们都能够接收who这个消息:

 

我们也可以证明所有对象的类都是A

当然,对某一个实例不存在的方法。另一个实例也不可能去响应。

 

深入Ruby的源代码,你可以看到,所有的Ruby对象结构上都有一个属性,叫做klass,说明了它所属的类:

 

图1-对象和它们的类

a1,a2,a3对象的klass属性指向同一个结构class A,而class A有一个属性m_tbl,实际上是一个Hash表,里面保存了这个类的所有实例方法,除了其它方法之外,我们刚刚定义的who也赫然在列,这个方法表叫做实例方法表。

当代码调用某一个具体对象的方法时,Ruby首先在它的class,也就是A的实例方法表中寻找,如果找到了,那么就调用这个方法。不然,通过一系列的搜索还没有找到这个方法,就抛出未定义方法错误(我们暂时不考虑method_missing)。因此,很容易理解,对a1,a2存在的方法,对a3这个对象当然也存在,而对a1,a2不存在的方法,对a3当然也不存在。

前面我说要经过一系列搜索,这个搜索就是超类。类是可以继承的,假设A是从B中继承下来的,那么A的对象实例就获得了B的实例方法。

在超类加入以后我们的对象和类关系增加了一层:

图2 对象-类—超类

在搜索一个对象方法的过程中,首先搜索这个对象本身类的实例方法表,如果没有找到,那么将搜索这个类的超类的实例方法表,一直向上搜索,直到超类为空(Object的超类为空).

在上面的例子中,当我们调用a3.where方法时,Ruby 首先搜索class A的实例方法表,发现其中没有where方法,因此它沿着类的超类向上,得到class A的超类class B,在它的方法表中找到了where.

但是,Ruby可以可以让我们定义只对a3存在的方法,例如

 

显然,a3现在已经有了special方法,但是,如果我们去调用a1和a2的special方法,那么结果还是一样的:

 但是,如果我们继续调用a3.who方法,好好在那里

Ruby设计者是怎样解决这个问题的呢?回忆我们前面讲到的实例方法的搜索路径,一个巧妙的设计诞生了:

我们从图中的虚线可以看到,原先a3和a1、a2一样,它的klass都指向class A,但是现在a3的klass指向一个新的class (A),而class(A)的超类是原先的class A,所有为a3对象单独定义的方法都放在这个class(A)的实例方法表中。因为它只为某一个对象所用,所以我们把这个class(A)称为单件类。

巧妙之处何在?巧妙在于,用老规则解决新问题。旧的搜索规则规定,对象的方法首先搜索这个对象所属类的方法表,如果找不到则搜索超类,直到搜索成功或者没有超类为止。这个设计完全遵照这一规则。

上图中,a1,a2没有任何变化,所以它们的行为依旧。但是对于对象a3而言,由于它的类指向了一个新单件类class(A),因此,当程序调用a3.special方法,ruby很快就从这个单件类中搜索到了special方法。而如果调用的是who或者where,由于无法在单件类中找到这些方法,那么就会沿着超类而上进行搜索,而它之上所有的结构都没有变化。因此,原先的所有方法依旧可用。更重要的是,不管A以及它的超类如何变化,所有这些变化都能够在a3这个对象中得到体现。


现在我们可以下一个结论,什么是单件方法,单件方法是在单件类中定义的方法?什么是单件类,用来存储单件方法的类就是单件类。它们都是为某一个具体的对象服务的。


从图中我们可以看到a3.klass指向的是这个单件类,那么a3.class的结果是什么呢?

很奇怪,依然是A。 Ruby在给出一个对象的class时候,只会给出它的“real_class”---不是单件类的类。从另外一个角度考虑,由于这个单件类没有任何名字,同时也不可能被实例化,确实并不需要明确地以.class的方式获取。


但是,很多时候我们还是需要在这个单件类内部进行操作。举个简单的例子,假设我想为a3定义很多方法,如果按照前面的方法,那么只能一个一个定义:

这样做非常繁复,而且无法给出一个统一的概念模型,因此Ruby提供了另外一种方法,能够让你“打开”单件类,并在其中操作。这就是我们在最开始提到的第2种定义类的方法:

obj是一个具体的对象实例,class << 代表它的单件类。