`
sizhefang
  • 浏览: 227784 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论

Ruby 单件类

    博客分类:
  • ruby
阅读更多
   非常感谢javaeye上的各位大牛,给我们这些小虾们总结了这么多入门的文档。把potian同学总结的作为收藏。

单件类(Singleton Class)
在Ruby中,定义一个类可以有两种方法:


class [ scope:: ] classname [ < superexpr ]
   body
end

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

对象和类

class A
    def who
        puts “I’m a 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 a instance of class A
a2.who        # I’m a instance of class A
a3.who        # I’m a 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当然也不存在。

超类
前面我说要经过一系列搜索,这个搜索就是超类。类是可以继承的,假设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 a instance of class A
a2.who            # I’m a instance of class A
a3.who            # I’m a 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.

单件类
但是,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 a instance of class A
Ruby设计者是怎样解决这个问题的呢?回忆我们前面讲到的实例方法的搜索路径,一个巧妙的设计诞生了:
 
我们从图中的虚线可以看到,原先a3和a1、a2一样,它的klass都指向class A,但是现在a3的klass指向一个新的sclass 而sclass的超类是原先的sclass,所有为a3对象单独定义的方法都放在这个sclass的实例方法表中。因为它只为某一个对象所用,所以我们把这个sclass称为单件类。

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

上图中,a1,a2没有任何变化,所以它们的行为依旧。但是对于对象a3而言,由于它的类指向了一个新单件类sclass,因此,当程序调用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 << 代表它的单件类。
[img][/img]
分享到:
评论
1 楼 allwefantasy 2009-05-27  
讲解的很细致。potian写的 帖子 “单件类”很不错 可惜不怎么适合我这种感接触ruby的人。

相关推荐

    详解Ruby中的单件方法和单件类

    ### 详解Ruby中的单件方法和单件类 在Ruby编程语言中,单件方法(Singleton Method)和单件类(Singleton Class)是两个非常重要的概念。这些特性为Ruby提供了强大的灵活性,允许开发者针对特定的对象定义唯一的...

    详解Ruby设计模式编程中对单例模式的运用

    简介  单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一... 从具体实现角度来说,就是以下三点:一是单例模式的类只提供私有的构造函数,二是类定义中含有一个该类的静态

    Ruby面向对象编程中类的方法与类的扩展

    类方法其实质是生活在该类的单件类中的单件方法。其定义方法有三种,分别是: # 法一 def MyClass.a_class_method; end # 法二 class MyClass def self.anther_class_method; end end # 法三* class MyClass class...

    Ruby元编程基础学习笔记整理

    - 类和对象都是Ruby中的第一类值。 **应用示例**: 以MongoDB的Ruby客户端为例: ```ruby # test_mongo.rb require 'mongo' require 'pp' include Mongo # 测试MongoDB服务器版本 2.6.0 host = "192.168.11.51" ...

    Ruby中钩子方法的运用实例解析

    通过使用钩子方法,可以让我们在Ruby的类或模块的生命周期中进行干预,可以极大的提高编程的灵活性。 与生命周期相关的钩子方法有...单件类相关 BasicObject#singleton_method_added BasicObject#singleton_method_r

    Ext Js权威指南(.zip.001

    4.4.5 管理类的类:ext.classmanager / 159 4.4.6 类创建的总结 / 161 4.5 动态加载的路径设置 / 163 4.6 综合实例:页面计算器 / 165 4.7 本章小结 / 169 第5章 ext js的事件及其应用 / 170 5.1 概述 / 170 ...

Global site tag (gtag.js) - Google Analytics