Ruby 总结一下 Ruby 中的对象和类模型

ftandy · 2015年04月26日 · 最后由 ftandy 回复于 2015年05月14日 · 8122 次阅读
本帖已被设为精华帖!

用 Ruby 也快有一年了,总结一下 Ruby 中对象和类模型,表达不是很熟练,请轻喷。

ruby 的对象和类模型到底是什么鬼?

在 ruby 的世界中,有这么三个原则:

  • 所有东西都是对象,除了 method 和 block(经过@chiangdi提醒,并不是 Ruby 中的任何概念都是对象呢,method 和 block 就不是。 )

  • 所有对象都有一个类

  • 类最终都继承自源类 Object,而 Object 继承自 BasicObject

在这个描述中,假设定义了一个类 Dog,实例化一个对象 tidy,对象 tidy 的类是 Dog,Dog 类的类是 Class, Class 继承自 Module,Module 继承自 Object,Object 最终继承自源类 BasicObject,BasicObject 也是对象,那 BasicObject 的继承自谁?

在这里我打开 irb,ruby 的版本为 2.1.2,进行操作。

class Dog ; end
tidy = Dog.new
tidy.class #=> Dog
Dog.class  #=> Class
Class.superclass #=> Module
Module.superclass #=> Object
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil

很明显,BasicObject 没有超类,这条贪食蛇吃到这里就终止了。 而 Dog 继承关系上的类概念:Dog,Class,Module,Object,BasicObject 的类都是 Class。

也就是说我可以创建这些类概念的实例对象,Dog 就是一只狗,Class 就是一个类,Module 就是一个模块, Object 就是一个对象,BasicObject 就是一个基础对象。而 Dog,Module 和 Class 自己本身都是 Class 类的一个 实例对象,可以去创建我们想要用的概念对象。

而 Object 和 BasicObject 包括一些对象的基本方法,例如 Object 的 nil?方法, 因为所有对象都会继承自 Object,所以所有对象都会有nil方法;又例如 BasicObject 的instance_eval方法,因为所有对象 也会继承自 BasicObject,所以所有对象也会有instance_eval方法。 这些方法都可以在ruby 的手册上看到。

这里我画了一个继承的图出来。

那 BasicObject 和 Object 有没有nil?instance_eval方法呢? 当然会有,因为他是 Class 的实例对象,对象都继承 Object 和 BasicObject, 他们都可以享有自己的方法。

而我们在讨论对象和类模型的时候一般都会忽略掉 BasicObject,因为他置于最顶层,我们不会轻易改变他们, 而只讨论 Object 以下的对象和类。

eigenclass

eigenclass 也叫元类或者单件类。eigen 的意思为本质的,换我们中国人古代的概念应该叫元神。
每个对象都有自己的 eigenclass,可以通过连个方法找到他。下面代码找出了一只泰迪狗和 Dog 类的元类。

def Dog ; end
tidy = Dog.new
tidy_eigenclass = class << tidy
   self
end 
tidy_eigenclass => #<Class:#<Dog:0x000000023bc5e8>>
tidy_eigenclass.class #=> Class
tidy_eigenclass.superclass #=> Dog

Dog_eigenclass = class << Dog
    self
end
Dog_eigenclass=> #<Class:Dog>
a = Dog_eigenclass.class=> Class
Dog_eigenclass.superclass=> #<Class:Object>
Dog_eigenclass == a #=> true

上面的代码的意思是:在泰迪狗和 Dog 类的之前还有一个 eigenclass,Dog 类与 Class 类之前还有一个 eigenclass。 经@lolychee同学提醒,其实可以通过class << XXXXXX.singleton_class两种方法来找到对象的元类:

Dog_eigenclass = class << Dog
    self
end
Dog_eigenclass=> #<Class:Dog>
Dog.singleton_class=> #<Class:Dog>

那元类有什么作用呢?
其实有了元类,当我们想要扩展对象和类自身的方法而非继承方法的时候,就变得非常容易,而且可以使用很多种方法来实现。

class Dog
  def self.bar
    "wowowo"
  end
end
class Dog
  class << self
    def bar
      "wowowo"
    end
  end
end
def Dog.bar
  "wowowo"
end
class << Dog
  def bar
    "wowowo"
  end
end

上面的定义代码使得 Dog 类都可以用Dog.bar来输出"wowowo"。
对类来说,可以使用class_eval来打开自己来操作类自己。 对对象和类来说,可以使用instance_eval来打开自己的 eigenclass 来操作自己。
其实自己再画一个继承图就很好理解了。

其实还有一个 nil 我还没有画,而 nil 的 class 是 NilClass。当定义一个 Dog,和实例一个对象 tidy 就成这样的图了。 这个只是局部的显示出 eigenclass 视图。有人做出了一个不包含 eigenclass 的 ruby 常用类的关系图,真心跪了。

总结

想要精通 ruby,完全熟悉 ruby 的对象和类模型是不可缺失的一步。

参考

请问,图是用什么软件画的?

非常好的文章,我再补充点: 文章中说的 eigenclass 其实有方法找出来,就是Object#singleton_class,自 1.9.2 版本时添加的方法。

Object.new.singleton_class  #=> #<Class:#<Object:0xb7ce1e24>>
String.singleton_class      #=> #<Class:String>
nil.singleton_class         #=> NilClass

然后,关系图这里也有一个 http://ruby-doc.org/core-2.2.2/Class.html

第一个原则: 所有东西都是对象 其实不是太准确的,比如 method 和 block 就不是对象,虽然他们可以转换成对象。应该说所有值都是对象准确点,Ruby 编程语言那本书就是这样说的。

@hhkbp2 是 gliffy,而且会员升级之后效果比较好。 @lolychee 已补充,感谢提出了错误。 @chiangdi 按照我的理解,定义一个 method 也是定义一个 Method 类对象吧。

b = "a".method("nil?")
b.class#=> Method

block 的话按照我的理解,也是一个 Pro 类的对象吧

def a(&b) p b.class end
a {}#=> Proc 

block 那块的话这个贴下有详细解释https://ruby-china.org/topics/10414

#5 楼 @ftandy 第一个例子,你只是调用了 “a” 的method 方法得到了一个 Method 对象,但是 nil? 本身不是一个对象。比如你这样定义一个方法

def hello
  hello
end

如果 hello 方法本身是个对象的话,应该可以直接通过hello.class 得到它的类。

至于 block,它本身不是对象,它是 Ruby 本身的一个语言特性,但是可以通过proc 或者lambda 转化为一个 Proc 类的实例,这一点很多书上多说的非常清楚了。

手机码字,好像讲得不太清楚。

mark,霸气的头像

@chiangdi 我理解是这样的,当你打开 irb 定义一个方法,所在的顶层作用域是 main 对象,main 的类是 Object。而在 main 中定义方法的时候,方法属于私有方法(这个我不是很清楚为什么是私有)。 无论你在哪个作用域,用 self 都可以找到自己并发现最终继承自 Object。所以 method 必须通过找到自己所在作用域的对象,然后通过a = self.method(:XXX)来找到自己,通过a.receiver找到自己的寄宿的对象,但无法在定义的时候def hello ; self end返回自己,只能通过寄宿的对象找到。

def hello
  "hello"
end
self #=>main
self.class #=>Object
a = method(:hello)
self.private_methods.include?(:hello)#=> true
self.methods.include?(:hello)#=>false, and only returns a list of the names of public and protected methods of obj
self.method(:hello).class #=>Method
a.receiver#=>main

非常奇怪的一点是当在 main 对象这个顶层作用域中定义方法时候,会在 main 对象和 Object 类中都会使这个方法私有化。这可能是在使用 irb 中的一个特性吧。

def lalala ; end
self.private_methods.include?(:lalala)#=> true
Object.private_methods.include?(:lalala)#=> true 

有关顶层作用域这篇文章分析得比较清楚What is the Ruby Top-Level?

#9 楼 @ftandy 这个不是 irb 中的一个特性,你在哪里写都是一样的,只要在顶层作用域定义方法都是 Object 类的私有方法。另外, .method(:XXX) 总会返回一个 Method 对象,就像.to_s 总会返回一个 String 对象一样,这样并不能说明方法本身是个 Method 对象。

我特地去找了 matz 写的《Ruby 编程语言》来,在第 203 页:

Ruby 的方法和代码块都是可执行的语言构件,但是它们不是对象 ... Object 类定义了一个名为 method 的方法,它接受一个字符串或者符号表示的方法名,返回的 Method 对象表示在 接受者对象中相应的方法。

关于 Ruby 的 Object Model,我强烈建议你看下《Ruby 元编程》和《Ruby 编程语言》这两本书,解释的很清楚。

@chiangdi《Ruby 编程语言》还没有看,看来是理解错误了,感谢纠正。

看到第一条时,突然想到当年学 Linux 时老师说的一句话,“在 Linux 上,一切都是文件……"

ruby 新手,感谢楼主贡献

13楼 已删除

刚好看完 Ruby 中的对象和模型这块,加深了理解,给楼主一个大大的赞

博主开篇说到:method 不是对象 我打开一个 IRB: irb(main):010:0* "aaaaaa".method(:length).is_a? Object => true irb(main):011:0> "aaaaaa".method(:length).class => Method

是否可以说明 length 方法是一个对象,它的类是 Method

@317583395 请看楼上的解释,已经说得很清楚了

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