Ruby [原创] Ruby 中 类 / 对象模型 的探索

zw963 · 2012年10月29日 · 最后由 zgm 回复于 2012年11月26日 · 5434 次阅读

不好意思,这其实是之前的一篇文章,改了个标题。

之所以这样做,是因为之前写的有点混乱,事实上之前自己也没完全理清楚,所以今天做了大幅度修改,这是一个试图将 Ruby类模型 理论化的尝试,有时候心里明白一件事将这件事清晰的讲出来, 是完全两码事,后者往往难得多。

无论你是否读过,不妨再次读一读,这次的概念,应该是清晰多了,事实上本人也经历了一个从模糊到清晰的过程,我并没有读过 C 的源码,但是这不妨碍 我猜它 ... 从 Ruby 的角度... 不对莫怪!

如果谁能指出不准确的地方,那就太感谢啦。如果大家都不感兴趣 (我承认大多数人应该不感兴趣), 权当做对自己之前的一个模糊的概念的修正吧。

什么叫做类对象?

类对象 只不过是 Ruby 内核中 Class类的一个实例 . 当你创建一个新类时,就会创建 Class 类的一个对象。下面的代码更清楚:

a_class = Class.new(Object)
Klass = a_class

等价于:

class Klass < Object
end

这里说的 又是什么?

这里的类,指的是 类定义, 而非类对象。为什么一定要这样区分?后面自然见分晓。

你只要明白,这里要讲的 这个概念,是一个面向对象的语言 (例如 Ruby ), 面向对象消息机制的实现方式..

到底那些东西属于真实的 类定义 呢?

似乎就只有以下三种对象,他们真实的属于 类定义, 在所有情况下,你只能通过 类对象类实例对象 的方式来引用这些定义的值。

1) 类的实例方法

类实例方法 是属于类自身的,这里有点名不符实,因为它是实实在在属于 类定义 方法,而不是 类实例之上定义的方法, 只不过你只能通过 类实例对象 的方式来引用它。

2) @@类变量

它有点像是作用在整个类继承层次的 全局变量, 它的行为像是没有命名空间的全局常量,在 一个类及其所有子类中 总是唯一的。既然他属于 类和它的子孙 公用的那一部分,那也必然应该属于类了,@@类变量是私有的,你只能在类定义内部隐式的访问它。

3) 类常量

类似于 类实例方法, 它属于也属于类定义,但是很明显,类常量同样可以通过域作用符 类对象::常量名(命名空间) 的方式,通过外部空间来访问它。

@@类变量 和 类常量 都只能通过 类对象 来引用它,不过他们的可见性不同,前者是 private, 后者是 public.

类实例变量是什么?

类实例变量,跟这里提及的 类定义 没有一点关系,他只是 类对象 的实例变量,他是 private 的,你只能在类定义内部隐式的访问它。

类实例变量类常量 非常相似,他们的共通点是:

他们都可以在类方法中被直接使用。(self 都指向了类对象), 事实上在创建一个类时,两者经常互相混用。

它们的不同点是:

  • 类实例变量是 private 的,而类常量是 public 的。
  • 类实例变量是 self(类对象) 专有的私有变量,因此在 `类实例方法' 中,是不可见的。

类方法是什么?

只不过是在类对象之上被定义的方法。跟类定义没有任何关系。(你有在类中直接使用类方法吗?) 可能发现了,又有一点名不符实。因为 类方法, 实实在在不属于 , 而是属于 类对象.

为什么要区分 类定义类对象

只有一个原因,属于类定义的部分,无论是 类方法 还是 实例方法 中都可以直接访问,你甚至可以理解为在这种情况下,会将 self 从 实例对象 反射到 类对象.

上面这一句话,就是我总结出来的就是 Ruby 类/对象模型所有魔法的根源。

概述:

类::常量 方式访问的是 类对象 的常量 类::方法 方式访问的是 类对象 的方法。

而我们通常在 类定义 内部定义的常量以及类方法, 前者是真实属于类定义,而后者跟 类定义 没有一点关系,只不过他们都可以在 self (即类对象) 之上被引用而已。

还有最后给出一个有趣的实例,直接看代码:

class MyClass
  CONSTANT = 100
  @@class_variable = 200
  @class_instance_variable = 300

  def self.hello
    p CONSTANT
    p @@class_variable
    p @class_instance_variable
  end

  def hello
    p CONSTANT
    p @@class_variable
    p @class_instance_variable
  end
end

MyClass.hello
puts "-" * 30
MyClass.new.hello

# 输出结果如下:
# 100
# 200
# 300
# ------------------------------
# 100
# 200
# nil

为什么会输出这样的结果呢?

你定义了一个 类常量, 一个类变量, 一个 类实例变量.

前两者是属于 类定义 , 而后者是完全属于 类对象 自己的实例变量,因此,即使在 类实例方法 中你也可以访问前两者,而后者则不同,它不属于类定义,它访问的是对象自身的私有实例变量,默认初始化为 nil.

尾声

我觉得我应该讲清楚了,从最初的原本只是想写几行,说说 Ruby 中一些有趣的事儿,到后来越写越多,最后成了一个分享,中间的过程,还是蛮乱的,不过我已经很努力了。

等有空的话,我希望希望再分享其他一些跟 Ruby 有关的心得,例如:继承,混入,扩展有关的一些知识。权当作为这篇文章的姐妹篇。

练习题

为了说明 我所讲的类概念 在实际的应用中有多么的重要,试着编写了几个练习题,如果你真的看明白了我的分享 (或者你原本就明白), 你一定可以轻松解答。

  1. 一个模块中,定义了 常量, 类变量, 实例方法, 类方法, 类实例变量, 当这个模块被混入一个类时,请问,那些数据可以被混入 ??

  2. 一个类中的那些东西,是可以被继承的?

我貌似懂了,又貌似还不是很懂。难道这就是传说中的似懂非懂

记得元编程里没有这么复杂呀

#2 楼 @williamherry

元编程书我没看过,不过元编程即使涉及类,也应该是主要谈及 继承,混入,扩展的一些知识。不会涉及类的实现。而我讲的是类的实现。

到现在为止不知道怎么区分类和对象这个东西!

匿名 #6 2012年10月30日

In ruby, eveything is an object, including class, module

匿名 #7 2012年10月30日

#3 楼 @zw963 there is no metaprogramming, metaprogramming is just a bunch of tricks that ruby provides to build some funny program and most of the time metaprogramming is used in Rails.

#7 楼 @nuclearkitten

如果你没有这些 继承, 混入, 扩展 的知识,何来元编程?你最多只能照葫芦画瓢的学别人用 instance_eval, define_method, send 以及那么一大堆 HOOK, 而且以我的经验来说,你会越看越糊涂。越用越不清楚...

btw: why do you speak english? are you a foreigner ?

匿名 #9 2012年10月30日

#8 楼 @zw963 you may read , this book is highly recommended.

匿名 #10 2012年10月30日

#8 楼 @zw963 I'm ZBing, to show off my english

@zw963 觉得 LZ 的解释太复杂,你指的类常量@@类变量都是指针指向的那片内存区域中的数据,其它的方法都属于类对象,可以通过结尾的例子理解^_^

#8 楼 @zw963 继承, 混入, 扩展 这个只能算类型系统吧...

#10 楼 @nuclearkitten
ZBing is what ? 呵呵。

#11 楼 @neverlandxy_naix

是的,写着写着就复杂了,主要是想解释的更清楚一点。
之前的内容是吃午饭前写的,最后那个例子是吃完午饭后才加的。

另外你说的也不对,类,类常量,类变量 都是引用对象, 他们也属于类对象。被引用的地方才是我说的那片 内存区域. 也许是你笔误,也许是你没有完全明白 Ruby 中的一切对象都是引用 ??

#12 楼 @bhuztez

反正就是元编程的基石啦。没这玩意儿,哪里来的 Rails.

匿名 #15 2012年10月30日

#14 楼 @zw963 zhuang x,

匿名 #16 2012年10月30日

#14 楼 @zw963 头像是谁?你孩子?

#16 楼 @nuclearkitten

哇,你说中文了...

是呀。我儿子,很听话的一个孩子。

匿名 #18 2012年10月30日

#17 楼 @zw963 感觉很活泼哦, ;D

#14 楼 @zw963 不能这么扯啊,不然的话,那真a = 1这样的语法都是元编程的基石了。类型系统,大多数时候和元编程没啥关系...

匿名 #20 2012年10月30日

#17 楼 @zw963 read the metaprogramming, you won't regret

#13 楼 @zw963 LZ 你的理解是对的^_^

#17 楼 @zw963 记得学前班的毕业照我摆的就是这个姿势....因为当时阳光太晃眼

hi, all, 我更新了本贴,概念应该清晰了许多!

我觉得这次,我真的应该讲清楚了...

#23 楼 @zw963 其实挺简单的吧

首先是类型系统,每个object都有类型。于是就有个objecttype,接着被Everything is an object忽悠了,于是type也是一个object了。

接着,Ruby 不让你直接访问另外一个object的成员变量。Singleton Class其实就是实例,而你从另外一个object访问的时候,instance.X其实就是instance.X()

#24 楼 @bhuztez

咳咳,是不是简单,那也分人呀。你老兄当然觉得简单啦。

不过你后半段,我是真没有看明白,你真的了解 Singleton Class 是怎么回事儿么?我不知道你是否读过 Programming Ruby 有关这部分的图例讲解?

#25 楼 @zw963 你先想一个简陋的类型系统,只有 type 和 object,接着 Ruby 就是在这个基础上,把 object 外面包了一层,不让你直接访问,必须要通过方法才能访问,就这么简单。

加的这层其实就是为了看上去符合,object 之间只能通过消息来相互访问,接着用方法来模拟消息传递。但是这种实现方式很不靠谱。

只要你理解 Erlang 的模型了,看这些都是一笑而过的啦。

#26 楼 @bhuztez

你说的没错,但是我能否说一句,你对 Ruby 在这一点的理解有点肤浅呀....

你拿一门纯函数式语言和 Ruby 相比,是不是有点不妥...

#27 楼 @zw963 如果你认为 Erlang 的重点是纯函数式,那你根本就没理解 Erlang,也没理解 OO。

#28 楼 @bhuztez

我是真不了解,可是 Ruby 的 OO 也没有你说的那么简单呀。包括 Singleton Class, Mixin, Extend, 这都有一套体系,的确不复杂,如果了解的话,但是很抽象,不看书,只是自己用,我觉得只会越用越糊涂,至少

Singleton Class 其实就是实例

我是没明白你是说什么。

@zw963 赞,分析的很清楚。

类方法 其实都是 Singleton Class 的实例方法

看这中文还没看英文来的明白。什么类方法,类对象方法,类实例方法,都绕晕了. 英文就清楚多了,class method, instance method. (Ruby 就这两种方法) One has self, one does not. the self is the object current message in.

And three types methods visibility is more complicated: public, protected, private.

#30 楼 @virgil

谢啦。只要有一个人这样说,至少证明,我能给你一个讲清楚,这就够啦!

#31 楼 @tumayun

这里讲的类概念,有空我会专门讲单例方法,

#32 楼 @stardiviner

说句实话,中文有时候真的很累,尤其是汉文化的博大精深,常常每个人都会有一套不太一样的说法,不像英文,来来回回就那几个单词,没有歧义。

#34 楼 @zw963 其实哪里都一样,不然就没 JARGON 了

#35 楼 @bhuztez

好吧,我承认,我是专门查了下 dict 才知道 JARGON 是什么的。貌似 JARGON 他自己就是一个典型的 JARGON ...

#27 楼 @zw963 #28 楼 @bhuztez 我不确定 B 大是否和我一个意思,不过我个人一直认为 erlang 非常 OO,所以类比一下无妨 另外个人感觉要讲清楚 oo 的话,一开始最好避免提 class,从 object 开始可能会好些

#37 楼 @fsword

嗯。erlang 是我不了解,我只是粗粗的在 松本行宏的程序世界 中 看过几行 erlang 代码,不甚了解。反倒是我可能接触面太窄了,呵呵。在 @bhuztez 看起来 , 貌似一切都是那么的简单,这也是偶的追求。事实上,很多学习过程我感觉都有 简单 - 复杂 - 更简单 的一个交互,也只有达到 更简单 这个境界,才可以融会贯通。

另外个人感觉要讲清楚 oo 的话,一开始最好避免提 class,从 object 开始可能会好些

这话虽然没错,不过不想明白 class 总是觉得少点什么。而且这不仅仅是 Ruby 语言,虽然由这些概念引申出来的结论,我想社区中,大部分人都知道,但是我想这样始终是一个 知其然不知其所以然 的死记状态,对吧。想明白了反倒是豁然开朗了。我刚刚又专门在最后,补了道练习题,用以说明 搞明白那些东西属于类 是多么的重要。

懂的人自然看懂了,没懂的人估计也难懂,当时这些东西在图书馆摸索了好久啊…

需要 登录 后方可回复, 如果你还没有账号请 注册新账号