Ruby对象模型与元编程(一)

 

Ruby看起来与众不同,基于一组精心设计的接口方法和作用域理论,形成了其优雅的语义模型。
但是万变不离其宗,了解了Ruby的对象模型,也就了解了其魔幻般的动态元编程原理。

[消息机制]

Ruby很好的继承和发扬了Smalltalk的面向对象理论。
在Ruby中,任何值是对象,原始类型如数字、字符串、布尔类型等是对象,甚至类和模块本身也是对象。
尽管对象的表示在形式上与一般数据类型十分相似,但是它们之间存在一种本质区别:对象之间通过消息传递方式进行交互。
对象交互的消息机制是面向对象更为本质的特征,为对象的交互提供了一种统一的形式,可以在运行时动态的创建消息,然后发送给合适的对象。

一般情况下,对象接收它能够识别的消息,拒绝它不能识别的消息。消息的名字就是这个对象公开的让外部可知的某个方法的名字。
由于类先于对象定义,
对象是类的实例,所以一个类为它的实例提供了可以预知的对外交互方式。


让我们想象一下如果发送一条对象不能识别的消息会怎样?
这种情况在C++、Java、C#等静态类型语言中会得到一个方法未定义的编译错误,在JavaScript中则会产生运行时异常,但是在Ruby这样的动态语言中却别有一片天地。

[对象模型]
在Ruby中,对象就是一组实例变量加上指向其类的引用。

对象的方法不存在于对象本身,而是存在于对象的类中。
类本身也是一个对象(Class类的实例),因此类也有自己的实例变量和指向其类的引用。

同时,类有一组实例方法和一个对其超类的引用。

正如你所熟悉的C++/Java等语言,典型的情况是先定义类,并创建一个该类的对象,然后向该对象发送消息。
在Ruby中,每一行代码都会在一个对象中执行,这个对象就是当前对象,用self表示。
在一个给定时刻,只有一个对象是当前对象。当前对象随着语句的执行而动态的变化,没有哪个对象能长期作为当前对象。
在任何时刻都知道哪个对象在充当self是绝对重要的。
Ruby中的类定义也是执行代码,而类也是一个对象,因此在类定义中,self就是正在定义的类。

同样的,在程序执行的过程中,也一直存在当前类。
Ruby会追踪当前类或模块的引用,所有使用def定义的方法都成为当前类的实例方法。
在类定义中,当前类就是正在定义的类。如果有一个类的引用,可以使用class_eval()打开这个类。

另一方面,Ruby允许给单个对象增加方法,称为单件方法。

也许你会想,如果每个对象都有自己的方法,那这些对象的类型不就乱套了吗?
事实上,面向对象封装的无非是数据和行为,要判断一个对象属于什么类型只需要关注对象表现出来的行为即可。这种将对象类型认为是其能响应的一组方法的概念称为duck typing。

我们知道对象的方法不存在于对象自身,那么对象的单件方法又存在于哪里?
为了优雅的解决duck typing和对象类型之间的矛盾,Ruby引入了eigenclass。
一个对象的单件方法实际上是与它关联的eigenclass的实例方法。而类方法,实际上就是类的单件方法。
每个对象(包括类)都有自己的”真正的类“,可能是eigenclass,也可能是普通的类。

那么对象什么时候有eigenclass? 
有2种情况,自动生成和显式生成。
在定义类的时候,Ruby会自动为你生成这个类的eigenclass,而其他的对象(包括模块)则需要显式生成。

普通的类与eigenclass之间又有什么关系?
一个对象的eigenclass的超类是这个对象的类,而一个类的eigenclass的超类是这个类的超类的eigenclass。
由于引入了eigenclass,Ruby的对象模型世界一下变得丰富多彩起来。

[方法调用]
说到方法调用,先来弄清楚方法修饰符public、protected与private。
Java中也有类似的方法修饰符,其中public作用一样,但是protected与private的区别很大:
Java中,任何方法都可以被同一个类型的其他对象调用;而Ruby中private方法不能被同一个类型的其他对象实例调用,因为不能明确指定一个接收者来调用私有方法。
Java中,private方法不能不继承;而Ruby中,不管protected还是private方法,都可以被子类继承。
为什么会有这样的行为差异呢?
Java强调继承关系,方法调用限定类继承的层次结构,但不限定同一类型的不同对象实例。
Ruby中一切皆对象,方法调用针对对象实例进行限定。不过虽然不能直接给私有方法指定接收者,也可以通过消息发送(send)来调用私有方法。

当调用一个方法时,也就是给一个对象发消息时,Ruby会怎样做?
首先,Ruby会把self设置为消息接收者,然后进入其”真正的类“进行方法查找,如果没找到则进入其祖先链一直往上查找。
如果找到了相应的方法,则以self为对象,调用该方法。如果一直到BasicObject都没找到这个方法,就会调用self的method_missing方法。
既然method_missing也是方法调用,那么又会进行新一轮的方法查找,所以覆盖method_missing方法就是元编程的一个重要手段。

经过方法查找,找到合适的方法后,就到了方法执行的阶段。
由于数据与方法是分开的,找到的方法只是可执行的代码而已。代码执行时需要一个执行环境,包括局部变量、实例变量、全局变量、常量、self等,也就是一组绑定。绑定存在于一个特定的作用域里面,而作用域会随着程序语句的执行进行动态切换。
Ruby有严格完整的作用域限制,程序会在三个地方进行作用域切换:类定义(class)、模块定义(module)、方法(def)。
可以使用Class.new()、Module.new()和define_method()分别代替class、module、def关键字,来避免作用域的切换。

搞清楚了对象、类、Eigenclass、作用域、消息、self、当前类等,也就搞清楚了Ruby的对象模型。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值