锁定老帖子 主题:Ruby 单件类
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-05-13
class T1 class << self def foo puts "singleton method foo" end end def self.foo puts "class method foo" end end 这两种方法是完全一样的,所以谁先定义谁就被覆盖,谁后定义谁起作用 第一种方式: class << self def foo puts "singleton method foo" end end 这个self就是T1这个类对象,因此class << T1里面的内容就是T1的singleton类,而里面的内容就是T1这个对象的singleton方法 def self.foo puts "class method foo" end 和普通对象的singleton方法不同,类的singlton类是一开始就存在的,它是干什么的,它用来存放只对这个类对象有意义的方法,什么方法是只对这个类对象有意义的方法,显然就是这个类的实例对象的类方法。类的singleton class就是meta-class 由于self是一个类对象,所以看起来有点复杂。但理解这个概念完全可以通过考虑一个普通的对象: obj = T1.new def obj.test .... end class << obj .... end 显然两种方法是一摸一样的 下面的描述可能能够让你更清楚一点。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-05-13
以下内容节选自<Ruby Under Rails>的初稿,请勿转载
单件类(Singleton Class) 在Ruby中,定义一个类可以有两种方法: class [ scope:: ] classname [ < superexpr ] body end class << obj body end 这两种方法的存在是一个事实造成的,Ruby不但可以定义适合普通的类,还可以为某一个具体的对象定义它特定的类。譬如,我们定义了一个普通的类A. 对象和类 class A def who puts “I’m an instance of class A” end end 现在你可以用A创建出很多对象,例如: a1 = A.new a2 = A.new a3 = A.new 由于a1,a2,a3都是A的实例,因此它们的行为都是一样的。譬如,它们都能够接收who这个消息: a1.who # I’m an instance of class A a2.who # I’m an instance of class A a3.who # I’m an instance of class A 我们也可以证明所有对象的类都是A a1.class #A a2.class #A a3.class #A 当然,对某一个实例不存在的方法。另一个实例也不可能去响应。 a1.special #NoMethodError: undefined method `special' for #<A:0x356f40> a2.special #NoMethodError: undefined method `special' for #<A:0x35369c> a3.special #NoMethodError: undefined method `special' for #<A:0x34de04> 深入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当然也不存在。 |
|
返回顶楼 | |
发表时间:2006-05-13
前面我说要经过一系列搜索,这个搜索就是超类。类是可以继承的,假设A是从B中继承下来的,那么A的对象实例就获得了B的实例方法。
class B def where puts "Here is class B" end end class A<B def who puts "I’m a instance of class A" end end a1 = A.new a2 = A.new a3 = A.new a1.who # I’m an instance of class A a2.who # I’m an instance of class A a3.who # I’m an instance of class A a1.where # Here is class B a2.where # Here is class B a3.where # Here is class B 在超类加入以后我们的对象和类关系增加了一层: 图2 对象-类—超类 在搜索一个对象方法的过程中,首先搜索这个对象本身类的实例方法表,如果没有找到,那么将搜索这个类的超类的实例方法表,一直向上搜索,直到超类为空(Object的超类为空). 在上面的例子中,当我们调用a3.where方法时,Ruby 首先搜索class A的实例方法表,发现其中没有where方法,因此它沿着类的超类向上,得到class A的超类class B,在它的方法表中找到了where. |
|
返回顶楼 | |
发表时间:2006-05-13
但是,Ruby可以可以让我们定义只对a3存在的方法,例如
def a3.special puts “I’m so special” end a3.special #I’m so special 显然,a3现在已经有了special方法,但是,如果我们去调用a1和a2的special方法,那么结果还是一样的: a2.special # undefined method `special' for #<A:0x1e41ac> (NoMethodError) a1.special # undefined method `special' for #<A:0x1e41e8> (NoMethodError) 但是,如果我们继续调用a3.who方法,好好在那里 a3.who => I’m an instance of class A 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的结果是什么呢? a3.class #A 很奇怪,依然是A。 Ruby在给出一个对象的class时候,只会给出它的“real_class”---不是单件类的类。从另外一个角度考虑,由于这个单件类没有任何名字,同时也不可能被实例化,确实并不需要明确地以.class的方式获取。 但是,很多时候我们还是需要在这个单件类内部进行操作。举个简单的例子,假设我想为a3定义很多方法,如果按照前面的方法,那么只能一个一个定义: def a3.singleton_method1 end def a3. singleton_method2 end def a3. singleton_method3 end def a3. singleton_method4 end ……. def a3. singleton_methodn end 这样做非常繁复,而且无法给出一个统一的概念模型,因此Ruby提供了另外一种方法,能够让你“打开”单件类,并在其中操作。这就是我们在最开始提到的第2种定义类的方法: class << obj ….. end obj是一个具体的对象实例,class << 代表它的单件类。 |
|
返回顶楼 | |
发表时间:2006-05-13
关于类的单件类--metaclass的特殊行为,以及module 的mixin,以后再说
|
|
返回顶楼 | |
发表时间:2006-05-13
下午发了一个帖,在家里读ruby for rails时读到一些不太明白的地方,后来读懂了就删了,不过还是谢谢potian这么详细耐心的回答!
以前programming ruby没看全就动手写ror,结果一知半解直到最近看ruby for rails,恍然大悟 |
|
返回顶楼 | |
发表时间:2006-05-14
引用 self << obj
.... end potian,好像没这种语法,还是class << obj的笔误? |
|
返回顶楼 | |
发表时间:2006-05-14
写错了,呵呵,谢谢指正
|
|
返回顶楼 | |
发表时间:2006-05-14
也就是说,当a1被创建时,它的singleton class并不存在,当为a1添加第一个singleton method时,才有singleton class的定义,并且继承自A?
|
|
返回顶楼 | |
发表时间:2006-05-14
没错,因为不需要,并且数量可能太大
但对于类对象是不一样的,它们一被创建的时候就有了singleton class,准确地说是metaclass 更详细具体内容,请参见RubyHackingGuide(可能比较难懂),或者RubyUnderRails(呵呵,可能会有这么一本书) |
|
返回顶楼 | |