论坛首页 编程语言技术论坛

Ruby 单件类

浏览 22566 次
锁定老帖子 主题: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


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

下面的描述可能能够让你更清楚一点。
   发表时间: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当然也不存在。
0 请登录后投票
   发表时间: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.
0 请登录后投票
   发表时间: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 << 代表它的单件类。
0 请登录后投票
   发表时间:2006-05-13  
关于类的单件类--metaclass的特殊行为,以及module 的mixin,以后再说
0 请登录后投票
   发表时间:2006-05-13  
下午发了一个帖,在家里读ruby for rails时读到一些不太明白的地方,后来读懂了就删了,不过还是谢谢potian这么详细耐心的回答!
以前programming ruby没看全就动手写ror,结果一知半解直到最近看ruby for rails,恍然大悟
0 请登录后投票
   发表时间:2006-05-14  
引用
self &lt;&lt; obj
....
end


potian,好像没这种语法,还是class &lt;&lt; obj的笔误?
0 请登录后投票
   发表时间:2006-05-14  
写错了,呵呵,谢谢指正
1 请登录后投票
   发表时间:2006-05-14  
也就是说,当a1被创建时,它的singleton class并不存在,当为a1添加第一个singleton method时,才有singleton class的定义,并且继承自A?
0 请登录后投票
   发表时间:2006-05-14  
没错,因为不需要,并且数量可能太大

但对于类对象是不一样的,它们一被创建的时候就有了singleton class,准确地说是metaclass

更详细具体内容,请参见RubyHackingGuide(可能比较难懂),或者RubyUnderRails(呵呵,可能会有这么一本书)
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics