www.appflying.com,iPhone and iPad, top Apps, drop price.
24

1.2.4 变量查找

在Ruby中有四种类型的变量,它们是全局变量、类变量、实例变量及局部变量。全局变量作用于全局,局部变量只作用于局部,因此下面的讨论均不涉及它们,因为它们不与Ruby的类系统进行交互。
注:还有常量,但是它们不应该被改变。(事实上可以改变,但是Ruby会发出警告。)

实例变量是与特定对象相关的。实例变量以一个@符号开始:@price既是一个实例变量。因为每个Ruby对象都有一个iv_tbl结构,所以任何一个对象都可以有实例变量。由于一个类也是一个对象,所以一个类亦可以拥有实例变量。下面的代码展示了如何访问一个类实例变量的方法:

class A
  @ivar = "Instance variable of A"
end

A.instance_variable_get(:@ivar) # => "Instance variable of A"

实例变量总是基于self指针指向的对象来解析的。上面的代码,因为self是在class A … end中定义A的类对象,所以@ivar又属于A的类对象。

但是,类变量则不同。任何一个类的实例都可以访问其类变量(就是以@@开头的变量)。类变量也可以被其类定义自身所引用。虽然一个类变量和实例变量相似,但他们不是一回事:

class A
  @var = "Instance variable of A"
  @@var = "Class variable of A"
  def A.ivar
    @var
  end
  def A.cvar
    @@var
  end
end

A.ivar # => "Instance variable of A"
A.cvar # => "Class variable of A"

在这段代码示例中,@var 与@@var被存储在同一个地方:即A的iv_tbl。然而,他们是不同的变量,这是因为他们具有不同的名字(@符被当作变量名的一部分存储)。Ruby提供了一些方法用于检查被访问的实例变量和类变量,以便保传递中的变量名具有正确的格式:。

A.instance_variable_get(:@@var)
# ~> -:17:in `instance_variable_get': `@@var' is not allowed as an instance
    variable name (NameError)

类变量的使用有点让人疑惑。因为,它们被共享于整个继承链中,所以在子类中改变一个类变量时其父类的类变量值也被改变。

>> class A; @@x = 3 end
=> 3
>> class B < A; @@x = 4 end
=> 4
>> class A; @@x end
=> 4

这可能有用,但也可能令人迷惑。通常来说,你需要的会是类实例变量和ActiveSupport提供的可继承类属性(class inheritable attributes)这两者之一,前者独立于继承链,后者以一种受约束定义良好的方式在继承链中进行值传递。

24

1.2.3 Metaid

why the lucky stiff”为Ruby元编程创建了一个极小的库,叫做metaid.rb。在任何需要元编程的项目中都可以引进这段非常有用的代码:

class Object
  # The hidden singleton lurks behind everyone
  def metaclass; class < < self; self; end; end
  def meta_eval &blk; metaclass.instance_eval &blk; end
  # Adds methods to a metaclass
  def meta_def name, &blk
    meta_eval { define_method name, &blk }
  end
  # Defines an instance method within a class
  def class_def name, &blk
    class_eval { define_method name, &blk }
  end
end

这个库在每个对象(函数调用接收者)上定义了四个方法:

  • metaclass 指向接收者的单例类(self)
  • meta_eval 与单例类的class_eval等价。在接收者的单例类上下文中执行给定代码块。
  • meta_def 在接收者的单例类中创建一个方法。如果接收者是一个类或者模块,那么创建出来的是类方法(即接收者的单例类的实例方法)。
  • class_def 在接收者中创建一个实例方法(接收者必须是类或者模块)。

Metaid简短的代码却带来了不小的便利。通过使用这些简洁的方式来引用和增强元类,你的代码将变得比到处都放置诸如class << self; self; end等代码更清晰。这些技巧越简洁易懂,你就越可能在自己的代码中恰到好处的使用它们。这个例子展示了如何使用Metaid来检查和简化对单例类的更改:

class Person
  def name; "Bob"; end
  def self.species; "Homo sapiens"; end
end

Person类的类方法作为其单例类的实例方法被添加:

Person.instance_methods(false)             # => ["name"]
Person.metaclass.instance_methods -
  Object.metaclass.instance_methods        # => ["species"]

通过使用Metaid中提供的方法,我们的方法定义可以编写为如下形式:

Person.class_def(:name) { "Bob" }
Person.meta_def(:species) { "Homo sapiens" }
24

Method Missing

解决了前几篇所有的疑惑后,理解method_missing就变得非常简单。其准则是:如果整个方法查找过程中,直到到达了Object都以失败而告终,那么将再次执行方法查找,但这次查找的是method_missing方法而不是原来调用的方法。如果method_missing方法被找到,它将被传入原方法的调用参数并调用,原调用的代码块也会被传入method_missing方法。

Object中的缺省method_missing(rb_method_missing)函数将抛出一个异常。

24

TextMate中文教程之在行,符号和书签之间移动

TextMate提供了快捷键让你明确定位。例如你想跳转到指定的行。利用快捷键Apple+L,然后输入行号再按回车就行。

这个方法是挺好,但是实际应用中很少有人会这么用,因为你不可能一直都知道你要去操作第几行吧。好在几乎所有的文档都有自己的组织形式。拿Ruby代码来说,它们都是在一个function或者method定义里的。TextMate会注意到这些,并且用快捷键辅助你迅速定位。

用鼠标点一下编辑窗口的右下角,那里列出了目前文档的header, class, method, 和 functio。随便选择一个,光标会定义到那一行的开头位置。看图:

但是在实际项目中,我们不会去常常拿鼠标选择这个地方。通常咱哥们都是用快捷键。Shift+Apple+T打开Navigation菜单中的Go to Symbol对话框。你可以用方向键来选择,也可以跟前面介绍的一样,使用缩写字母来选择某一个方法。例如输入s w r会选择save_without_revision( )。

如果这些都不能满足你的需要,你可以自己定义书签。定义书签很简单,你可以用Apple+F2键为当前行定一个书签,或者用鼠标点行号前面那个位置。看下面的图。利用快捷键Option+Apple+B可以显示或者隐藏那个书签栏。如果当前页面你已经定义了几个书签那么可以使用F2或者Shift+F2在书签之间顺序或倒序切换。

24

TextMate中文教程之移动光标

当你停止打字的时候,可能是需要操作光标了。发现上一行有一个错字,你有两个选择。要么用鼠标移动过去,要么用键盘移动光标到该错字的位置。你通常用哪个呢?想想,你的右手离开键盘到鼠标的时间再移动回来。每天你要为此多付出多少时间呢?所以用键盘来进行这一操作无疑是个好主意,TextMate也为这一过程提供了很多快捷键。别担心,他们都是很容易记忆的。

这有一些白痴都知道的操作计算机的通用快捷键:

方向键上 - 光标移动到上一行
方向键下 - 光标移动到下一行
方向键左 - 光标往左移动一个字符
方向键右 - 光标往右移动一个字符

如果这些快捷键加上一个Apple键的话,那么它就会移动到一行甚至文档的边界了:

Apple+方向键上 - 光标移动到文档的开头
Apple+方向键下 - 光标移动到文档的结尾
Apple+方向键左 - 光标移动到本行的开头
Apple+方向键右 - 光标移动到本行的结尾

你也可以使用Option键

Option+方向键上 - 光标移动到列的开头
Option+方向键下 - 光标移动到列的结尾
Option+方向键左 - 光标移动到当前单词的开头
Option+方向键右 - 光标移动到当前单词的结尾

这里列的概念你可能搞不清,看一下下图,按Option+方向键下光标会从 “first” 移动到 “data”。

如果你是个程序员,可以试试用Ctrl代替Option。他会在 CamelCaseWords 和 snake_case_words 这类的单词之间切换。第一种呢,会依次切换光标到C,C,W。第二种呢会依次切换到s,c,w。

可能你是一个emacs的支持者,没关系TextMate也支持那些你常用的快捷键:

Ctrl+P - 光标移动到上一行
Ctrl+N - 光标移动到下一行
Ctrl+B - 光标向左移动一个字符
Ctrl+F - 光标向右移动一个字符
Ctrl+A - 光标移动到该行开头
Ctrl+E - 光标移动到该行结尾
Ctrl+V -光标移动到文档结尾

这一章介绍滴这些快捷键在mac中的其他程序里面也能用到。比如Mail和Safari,所以好好记住它们吧。有个小提示,最后介绍的这些快捷键有可能跟你使用的TextMate的插件包冲突啊。比如Ctrl+P在Rails这个插件包就是prams[:id]的快捷键了。

23

TextMate中文教程之限制文件夹引用

当把文件夹引入TextMate项目中的时候,你可以过滤一部分不需要的文件和文件夹。如果你的项目是通过分组来组织项目文件的话,这个功能你就用不了啦,当然你也可以手动来完成。选中你项目边栏中的顶级文件夹,然后点边栏右下角那个 information按钮,看图:

这里有两个地方可以输入正则表达式来过滤,用正则来过滤掉那些你不希望出现在边栏列表里面的文件吧。贼拉方便。你也可以修改TextMate的属性,用快捷键Apple+,打开属性窗口选择文件夹设置。你可以在这里同样用正则来修改其中的内容。它不会影响目前的项目。

何时用这个功能?比如我有一个Rails项目叫做simple。它位于版本控制之下,我可以过滤掉这个项目中的script,log,tmp和doc文件夹。至少我一直都是这么干的。

23

TextMate中文教程之项目的文件夹引用

TextMate 支持两种项目。前面那章创建的是第一种项目,不管文件在硬盘上存在哪里只把它拖入到TextMate的边栏就可以。另外一种是保存文件系统。当你创建的项目文件在同一个文件夹里面,就创建了第二种项目。

项目中的文件夹引用的是硬盘上的真实对象。这有两个好处。第一,当应用程序得到焦点的时候 TextMate 会自动扫描该文件夹,如果其中的内容有更改则自动更新文件。其次,边栏下面那个文件夹按钮会在硬盘上创建真实的文件夹。新创建的文件夹在你当前所选的文件夹之内。

所以,你可以直接在TextMate里面操作和管理文件,而不用再跑到Finder里面去。所以我觉得还是第种组织项目的办法更棒。你慢慢会发现,整个项目处于同一个顶级文件夹是最方便的。因为你可以很方便的用svn来提交或者更新项目。很难想象,你把项目放到不同的文件夹去你该如何更新?麻烦死你!以后我会继续翻译如何用TextMate操作SVN命令,不过估计至少得2个月之后。哈哈。

那什么时候采用文件和组的项目呢?当你想操作不同文件夹下面的文件或者想在边栏中从新排列文件但是不想影响硬盘上的实际对象时。读者可能被我绕晕了,没关系举个例子。假如你是一个CSSer,每天的任务可能就是给公司的10个项目修改CSS,那么你就可以建立一个名为CSS-Work的项目,把不同项目的css文件都拖放到边栏中,并且根据轻重缓急组成不同的分组。明白了?

不论你采用的是哪种方式来组织项目文件,都可以使用边栏下面的Rename 或者 Remove Selected Files按钮。注意,根据你文件的组织方式不同。Remove Selected Files按钮可能仅仅是把文件从边栏里面删除或者是真的把它们扔到Trash去。

当然你也可以用边栏下面那个创建文件的按钮,如果你使用的是文件夹引用的方式来管理项目文件。那你在TextMate里面创建的文件就会在你边栏所选的那个文件夹里面创建,同时TextMate会提示你在本地保存文件。

23

1.2.2 方法查找(2)

分类:default | 给我留言 |

单例类

单例类(又称元类或者本征类,请参阅下面的“单例类术语”解释)允许一个对象的行为能够区别于同一类的其他对象的行为。你以前可能已经见过打开单例类的一些语句了:
单例类术语

将元类这个术语用在单例类上似乎并不很准确。当称一个类为“元”的时候意味着它在某种意义上比普通的类更抽象。但也有例外,单例类仅仅是属于某特定实例的一个类。

真正的元类见于诸如提供了丰富的元对象协议的Smalltalk等语言中。Smalltalk的元类本身就是类而且其实例也是类。同样的,Ruby中唯一一个元类是Class,因为在Ruby中的所有类均是Class的实例。

另一个较普遍的用于替代单例类的术语叫做本征类,它源于德语eigen(意为“它自己的”)。一个对象的元类就是它的本征类(它自己的类)。

class A
end

objA = A.new
objB = A.new

objA.to_s # => "#<A:0x1cd0a0>"
objB.to_s # => "#<A:0x1c4e28>"

class >> objA #打开objA的单例类
  def to_s; "Object A"; end
end

objA.to_s # => "Object A"
objB.to_s # => "#<A:0x1c4e28>"

class >> objA 表示打开objA的单例类。添加到单例类的实例方法与查找链路上的实例方法是一样的。其数据结构如图所示

上例中的objB是类A的一个实例。如果你问Ruby解释器,它将会告诉你objA也是类A的实例:

objA.class # => A

然而这仅仅是表象,其背后隐藏着其他的一些情况。另一个类对象已经被插入到查找链路中了。它就是objA的单例类。本文当中将其标识为“Class:objA”。Ruby也给他起了一个相似的名字:#<Class:#<A:0x1cd0a0>>。与所有的其它类一样,单例类的klass指针(未显示出来)也指向Class对象。

单例类在此被标记为虚类(flags中的一个标记位被用来标识其为一个虚类)。虚类不能被实例化,而且一般来说在Ruby中看不到它,除非我们特意去找它。当我们问Ruby objA的类是哪个的时候,它将跟随klass和super指针的继承路径上溯到第一个非虚类为止。

因此,Ruby将告诉我们objA的类是A。需要记住的一点是:一个对象的类可能与klass指向的对象并不一致。单例类之所以称其为单例是是因为每个对象仅有一个单例类。这样,我们才能够毫无歧义的找到“objA的单例类”或者Class:objA。在我们的代码中可以假设单例类确实存在;但事实上考虑到执行效率,它只有在第一次使用的时候才被创建。

Ruby允许单例类被创建在除了Fixnums和symbol的任意对象上。Fixnums和symbol是立即值(immediate values)(为了提高执行效率,它们本身将被直接存入到内存中,而不是用一个指针指向其数据结构)。因为它们自身被保存了进去,所以它们是没必要拥有klass指针的,进而也没有办法改变其方法查找路径。

你可以为true,false或者nil创建单例类,但是被返回的单例类与该对象原来的类一样。这些值分别是TrueClass,FalseClass及NilClass的单例实例(仅有的实例)。当你询问true的单例类是什么的时候,你将会得到TrueClass,因为立即值true是TrueClass唯一可能的实例。在Ruby中:

true.class # => TrueClass
class << true; self; end # => TrueClass
true.class == (class << true; self; end) # => true

类对象的单例类

这一节会比较复杂。要时刻记得方法查找的基本规则:首先Ruby根据对象的klass指针指向的对象进行方法查找;然后Ruby继续沿着查找链中的super指针的方向查找,直到找到合适的方法或者到达到最顶层。

需要牢记的另一重要事项就是类本身也是对象。就像传统的对象有一个单例类一样,类对象亦有其自己的单例类。这些单例类也可以象所有其他对象一样能够有各种各样的方法。既然一个类对象可以通过其klass指针来访问它的单例类,那么单例类的实例方法也就是这个类对象的类方法。下一段代码的完整数据结构如图所示:

class A
end


类A继承自Object。A的类对象就是Class类型。Class继承自Module而Module继承自Object。存储在A的m_tbl中的方法是A的实例方法。那么当在A上调用一个类方法将发生什么呢?

A.to_s # => "A"

将A作为接收者并以相同的方法查找规则进行方法的查找。(记住,A是一个代表A的类对象的常量)首先Ruby根据A指向Class的klass指针,在Class的m_tbl中查找名字为to_s的方法。没有查找到,Ruby继续跟随Class指向Module的super指针,在此处找到了to_s方法(在原始代码中为rb_mod_to_s)。

请不要感到惊奇,也没什么不可思议的。类方法可以被像实例方法相同的方式查找的到——唯一不同的是判断接收是类还是类的一个实例。

到现在为止我们已经知道类方法是如何被查找到的,这给了我们这样一种印象,那就是,似乎可以通过在Class对象(将它们插入到Class的m_tbl)上定义实例方法来达到在任何类上定义更多类方法。事实上,确实也是这样的:

class A; end
# from Module#to_s
A.to_s # => "A"
class Class
  def to_s; "Class#to_s"; end
end
A.to_s # => "Class#to_s"

这是个非常有趣的小技巧,但实用程度很有限。通常我们希望在每个类中定义其独有的类方法。在这个时候类对象的单例类就要被用到了。为了开启一个类的单例类,只需要在定义单例类的语句中将类名当作对象传递给进去就可以了:

class A; end
class B; end
class <<A
  def to_s; "Class A"; end
end
A.to_s # => "Class A"
B.to_s # => "B"

其结果数据结构如图所示。为了清晰起见,在这里类B被省略了。

to_s方法被加到A的单例类或者Class:A。现在,当调用A.to_s的时候,Ruby将跟随A指向Class:A的klass指针并且调用相适应的方法。

在方法定义中还有一点需要指出:在类或者模块的定义中,self总是指向类或模块对象:

class A
  self # => A
end

因为在内部A与self均指向同一个对象,所以在A的类定义中,class << A 也可以被 写为class << self。这种惯用法在Rails的类方法定义中随处可见。下例将展示定义类方法的所有方式:

class A
  def A.class_method_one; "Class method"; end
  def self.class_method_two; "Also a class method"; end
  class << A
    def class_method_three; "Still a class method"; end
  end
  class << self
    def class_method_four; "Yet another class method"; end
  end
end
def A.class_method_five
  "This works outside of the class definition"
end
class << A
  def A.class_method_six
    "You can open the metaclass outside of the class definition"
  end
end

# Print the result of calling each method in turn
%w(one two three four five six).each do |number|
  puts A.send(:"class_method_#{number}")
end
# >> Class method
# >> Also a class method
# >> Still a class method
# >> Yet another class method
# >> This works outside of the class definition
# >> You can open the metaclass outside of the class definition

这也意味着在一个单例类的定义中——就像在任何其他类的定义一样——self指向被定义的类对象。既然代码块或类定义的返回值是最后一条语句执行的返回值,那么class << objA; self; end 的值就是objA的单例类。代码class << objA将开启单例类,self(即单例类)就是从类定义中返回的。

将他们放在一起,我们能够开发Object类,可以添加一个实例方法到那些能够返回对象单例类的对象上:

class Object
  def metaclass
    class << self
       self
    end
  end
end

此方法奠定了Metaid的基础,稍后将详述之。

23

1.2.2 方法查找(1)

分类:default | 给我留言 |

Ruby中的方法查找可以让人很困惑,但它却非常有规律。理解复杂情况的最简单方法就是将Ruby在后台创建的数据结构直观化。每个Ruby对象在内存中都有一个域的集合:
注:除了立即对象(immediate objects),如Fixnums,symbols,true,false和nil;我们在后面会介绍这些。

  • klass 指向这个对象的类对象的指针。(它是klass而不是class,这是因为后者在Ruby和C++中是保留字;如果叫做class,Ruby将用C编译器而不是用C++编译器编译。这些有意的误拼在Ruby的每个地方都有使用)。
  • iv_tbl “实例变量表”,是一个包含了属于此对象实例变量信息的哈希表。
  • flags 一个比特组,含有一些状态信息的布尔值,例如对象是否被污染,垃圾收集标记位,以及对象是否被冻结等。

每个Ruby类或模块不但具有上边的这些域,它们还有另外两个域:

  • m_tbl “方法表”,一个包含了类或者模块中的所有实例方法信息的哈希表。
  • super 指向此类或者模块的超类的指针。

这些域在方法查找中扮演重要的角色,理解这些都非常必要。要特别指出的是,你应该密切关注klass与类对象的super指针之间的区别。

规则

方法查找规则非常简单,但需要知道Ruby数据结构是如何工作的。当一个消息发送到一个对象时,将会有下面的这些步骤依次发生:
注:Ruby中通常使用Smalltalk的消息传递术语,当一个方法被调用时,被叫做在发送一个消息,消息接收者是这个消息被发送到的对象。

  1. Ruby 根据接收者的klass 指针指向的对象中搜索m_tbl 中的方法并进行匹配。(klass 指针的目标总是一个类对象)
  2. 如果没有发现匹配的方法,Ruby将进而根据此类对象的super指针指向的超类m_tbl中继续搜索。
  3. Ruby以这种方式处理直到方法被找到或者直到到达了超类链的顶端。
  4. 如果在此链的所有对象上都未发现有匹配的方法,Ruby在原有方法调用者上调用method_missing 方法。这将重新开始上边的那些处理过程,但这次是查找method_mssing而不是原来的那个方法了。

这些规则应用的很普遍。所有这些在方法寻找中涉及到的有趣事情(混合,类方法,及自由类)都是因为有了klass和super指针。现在开始将更细致的讨论这个过程。

类继承

方法寻找过程可能会很让人困惑,所以我们要从简单的开始。下面的代码是Ruby中最简单的类定义:

class A
end

这段代码在内存中将生成下面这样的数据结构

上图是单个类的数据结构。双线边的方框表示类对象——它的klass指针指向Class 对象。A的super指针指向的是Object类对象,表示A继承了Object。为了清晰起见,从现在开始我们将在不会引起歧义的地方忽略指向Class,Module及Object的默认klass指针。

其次简单的情况是从一个类进行继承。类的继承简单的遵循了super指针的指向。例如,我们将创建一个继承自类A的类B:

class B < A
end

这段代码的最终数据结构如图

super 关键字总是指示出方法查找的链路方向,以下面的代码为例:

class B
  def  initialize
    logger.info “Creating 8 object”
    super
  end
end

在initialize方法中的super调用将遵循标准的方法查找链,以A#initialize开始。

类的实例化

现在我们有机会看看方法查找是如何进行的了。首先创建类B的一个实例:

obj = B.new

这将创建一个新的对象并且设置其klass指针指向B的类对象(见图)。

在单边方框中的obj表示一个传统意义上(plain-old)的对象实例。需要注意的是图中的每个方框都是一个对象实例。不过双线方框代表的是Class类的实例对象(因此它们的klass指针指向Class对象)。

当发送一个消息到obj时

obj.to_s

将遵循下面的这些链路:

  1. obj的klass指针将传递到B;在B所拥有的方法(在m_tbl中)中查找匹配的方法。
  2. 如果在B中没有找到相应的方法,那么将根据B的super指针指向的对象进行搜索,也就是在A中搜索相应的方法。
  3. 如果在A中仍然没有发现有相应的方法,那么将在A的super指针指向的对象中进行查找,即在Object中进行查找。
  4. Object类在其原生代码(rb_any_to_s)中含有to_s方法。它将被调用并且产生一个类似于“#” 的值。rb_any_to_s 方法检查接收者的klass指针以便决定要显示哪个类的名字;因此,虽然被调用的方法存在于Object中但仍然显示为B。

模块的引入

当我们利用模块进行mixin的时候情况将变得更加复杂。Ruby使用ICLASSes 来处理模块的引入,这是模块的代理。当将一个模块引入到类的时候,一个代表了被引入模块的ICLASS 将被Ruby插到引用此模块的类对象的super链路上。

在下面的模块引入示例中,为了简单起见这里暂时忽略了B的存在。先定义一个模块并将其混入A,这将生成如图1-4的数据结构:

module Mixin
  def mixed_method
    puts "Hello from mixin"
  end
end
class A
  include Mixin
end


从这儿开始ICLASS将登堂入室开始发挥起作用。从A指向Object的super链路被一个新的ICLASS阻隔了开来(即图中的虚线方框)。ICLASS是Mixin模块的代理,它包含指向Mixin的iv_tbl(实例变量)和 m_tbl(方法)的指针。

从图上可以很容易的看得出为什么需要代理类;这是因为同样的一个模块可能被任意多个不同的类混合而且这些类可能继承自其它的不同类(这样它们就有了不同的super指针)。将Mixin直接插入到查找链路是不合适的,因为当他被两个拥有不同父类的类混合时其super指针将不得不指向两个不同的物体。

实例化后的A类,其结构如图所示:

objA = A.new


在objA上调用混合方法mixed_method:

objA.mixed_method
#>>Hello from mixin

将有下面的方法查找过程发生:

  1. objA的类,即在A专用查找匹配的方法。将不会找到匹配的方法。
  2. 根据A的super指针指向的Mixin的代理ICLASS中进行查找,看看是否有匹配的方法。因为代理的m_tbl与Mixin的m_tbl一模一样,所以mixed_method方法将被查找到并进行调用。

在很多种编程语言中的多重继承都深受菱形难题的困扰,它是指当解析一个对象上的方法调用的时候发现此对象的类拥有一个菱形的继承路径,而这将导致解析过程中出现不二意性,如下图所示。

如图,如果通过类D的一个对象调用定义在类A中的一个方法,而类A中的这个方法被B和C都进行覆盖,这样就产生了歧义,系统不知道到底调用哪个方法了。Ruby是通过线性化引入的顺序来解决这个问题的。当进行方法调用的时候,查找链以线性的方式搜索,当然也包括那些被插入到查找链路中的ICLASS。

首先,Ruby不支持多重继承;然而,多个模块可以被混入到类中或者其他的模块中。因此,A、B、及C必须是模块。可以看出这样就没有歧义发生了,这是因为被选择为调用的方法是最后一个被插入到查找链路的那个:

module B
  include A
  def hello
    "Hello from B"
  end
end
module C
  include A
  def hello
    "Hello from C"
  end
end
class D
  include B
  include C
end
D.new.hello # => "Hello from C"

如果将引入的顺序改变一下其输出结果也会相应的改变:

class D
  include C
  include B
end
D.new.hello # => "Hello from B"

在最后的这个示例中,B被最后引入,那么其对象图将与如下图所示的一致(简单起见,指向Object与Class的指针在此被省略了)。

23

1.2.1 类和模块

分类:default | 给我留言 |

类(class)和模块(module)是Ruby面向对象编程的基础。类主要负责封装及分离事物。模块则用于mixin——将一系列功能打包添加到类上为该类增加新行为,可以代替多重继承。模块也可用来将类分隔到不同的命名空间中。

在Ruby中,每个类名都是一个常量,因此Ruby要求类名必须以大写字母开始。此常量代表类对象,也就是一个Class类的对象,但它区别于“Class”对象,后者表示真正的Class类。当我们说“class object”(小写的c)的时候是指任何一个表示类的对象(包括Class自己)。当我们说“Class object”(大写的C)的时候是指Class 类,它是所有类对象的超类。
注:如果我们还不够迷糊的话——Class对象也有Class类。

Class类继承自Module类;每个类也都是一个模块。但是有个非常重要的区别。类不能mixin到其它类中,也不能扩展对象;只有模块可以这么做。

  • 信息发布
macbook pro



关于 里克
里克:本名李玮,全职SOHO开发,技术咨询
邮件:hi@liwei.me
QQ:5175486
MSN:liwei78@live.com
05年开办公司,从事企业网站开发及维护,后从事社会化搜索引擎研发,08年底至今从事sns开发。近期专注Rails3,工作笔记整理及iphone开发学习。

狂爱Fm系列游戏。喜欢研究电影。关注IT新闻。写原创剧本和使用the movies拍摄短片,剪辑能力极强。美剧迷。狂热的英超曼城球迷。32岁的东北人。