`

Ruby之对象模型的大一统

 
阅读更多
当前类
不同于JAVA等静态语言,类定义中只能执行定义变量和方法的语句,在Ruby中,类定义的代码和其他的代码是一样的,可以在其中执行任何的Ruby语句。
result = class MyClass
	puts 'Hello'   # => Hello
	self
end
puts result # => MyClass


在Ruby中,不管代码执行到哪个位置,都会有一个当前对象self,相对应的,也总会有一个当前类的存在。当定义一个方法时,该方法就会成为当前类的一个实例方法。跟踪当前类在Ruby中也并不困难,当使用class或module关键字打开一个类的时候,当前类就是被打开的那个类,在类定义时,当前对象self和当前类都是类对象本身,在调用方法时,当前对象self是调用方法的实例对象,当前类是该实例对象的类。
class MyClass
  def my_method
    def inner_method
      puts "inner method"
    end
  end
end
obj = MyClass.new
obj.my_method
obj.inner_method  # => inner_method


在顶层对象时,当前对象self是Object类的实例对象main。在定义方法时,所有在顶层创建的方法,都会成为Object类的实例方法,但是,该方法被设置成了private访问权限,因此,你无法为该方法显式的指定一个调用者,但是可以在对象内部中调用该方法。
require File.dirname(__FILE__)+ '/my_class'
puts self.class      # => Object
puts self.to_s       # => main
def my_method
  puts 'My Method'
end
Test.new.test          # => My Method
Object.new.my_method   # => private method `my_method' called for #<Object:0x007f8dfa03a3b0> (NoMethodError)

class Test < Object
  def test
    my_method
  end
end


之前讲过,可以使用class关键字打开一个类,但是这必须首先知道类的名字。 Ruby提供了方法Module#class_eval()方法来处理不知道类名,却想打开一个类的情况。
def add_method_to(className)
  className.class_eval do
    def add_method
      puts 'Add Method'
    end
  end
end
class MyClass
end
add_method_to(MyClass)
MyClass.new.add_method  


实例变量
Ruby中在变量名前加前缀‘@‘即表示为实例变量,实例变量隶属于定义该变量时的当前对象self,因此类定义时的实例变量属于类,实例方法定义时的实例变量隶属于调用该方法的对象。
class MyClass
  @var =1
  def write
    @var = 2
  end
  def read
    @var
  end
  def self.read
    @var
  end
end
obj= MyClass.new
obj.write
puts obj.read       # => 2
puts MyClass.read   # => 1


在Ruby中,类实例变量只能被类本身访问,类的实例和子类都不能访问。所以千万不要把类实例变量等同于Java世界的静态变量了。
单件方法,类宏
在Ruby中,可以针对某个实例对象添加方法,这样,该扩展就不会对该类的其他实例对象产生影响,这种只针对单个对象生效的方法称之为’单件方法‘(singleton method)。
str = "he"

def str.double
  self * 2
end

puts str.double          # => hehe

another_str="another"
puts another_str.double  # => undefined method `double' for "another":String


在Ruby中,可以在类定义中使用一些类方法,这些方法的使用看起来很像关键字,这种方法一般称之为类宏(Class Macro),Module#attr_accessor方法就是一个典型代表,开发者也可以定义自己的类宏。
class Matcher
  def match
    puts 'match'
  end

  def match_safely
    puts 'match safely'
  end

  def self.deprecated(old_method, new_method)
    define_method(old_method) do |*args, &block|
      warn "Warnning: #{old_method} is depracated, please use #{new_method} instead!"
      send(new_method, *args, &block)
    end
  end

  deprecated :match, :match_safely
end

Matcher.new.match                        # => Warnning: match is deprecated, please use match_safely instead!
                                         # => match safely


在Ruby这样的动态语言中,对象的类型只是一组该对象能响应的方法,对象可以使用单件方法继续扩展该对象而不受类型的限制。在本系列第二篇:方法的妙用一文中,描述了Ruby中方法调用的过程,“向左一步进入该对象的类,然后沿着祖先链一直查找方法,找到方法之后,根据自身的绑定执行该方法”。


因此,对象本身只有一组绑定,而方法定义都是在类中。那么上面说到的单件方法和类宏应该在什么地方定义呢? 单件方法肯定不能定义在类中,否则将会影响该类的所有实例对象。类本身也是对象,类的方法不能定义在自身,因为对象的方法必须定义在对象的类中,而类对象的类是Class,如果把类方法定义到Class上,那么所有的类对象都会拥有该方法。这一切迷思的答案都来源于一个Ruby中的高级概念,Eigenclass
Eigenclass
在Ruby中,当调用obj.class向一个对象索要它的类的时候,编译器并没有告诉大家全部的真相,你得到的类并不是你看到的类,你得到的是一个对象特有的隐藏类,这就是该对象的Eigenclass,虽然Object#class方法想把关于Eigenclass的信息隐藏起来,但是,存在即存在,总会被人挖出来的。
obj = Object.new
eigenclass = class << obj
  self
end
puts eigenclass.class       # => Class


Eigenclass是一个类,但是是一个很特殊的类,它只能有一个实例,且不能被继承,但是其自身可以继承其它类。因此,所有的对象都有一个自己的Eigenclass,单件方法就定义在该对象的Eigenclass中,类宏定义在该类对象的Eigenclass中。

既然Eigenclass是一个类,那么其继承体系是怎样的?
module A
  def test
    puts 'test'
  end
end

class GrandFather
  def self.eigenclass
    class << self
      self
    end
  end
end

class Father < GrandFather
  include A
end

obj = Father.new

obj_eigenclass = class << obj
  self
end
puts obj_eigenclass                      # => #<Class:#<Father:0x007f8fe5037a38>>
puts obj_eigenclass.superclass           # => Father
puts obj_eigenclass.method_defined? :test    # => True

puts GrandFather.eigenclass              # =>#<Class:GrandFather>
puts GrandFather.eigenclass.superclass   # =>#<Class:Object>
puts GrandFather.eigenclass.method_defined? :test    # => false

puts Father.eigenclass                   # =>#<Class:Father>
puts Father.eigenclass.superclass        # =>#<Class:GrandFather>
puts Father.eigenclass.method_defined? :test    # => false


class BasicObject
def self.eigen
  class <<self
    self
  end
end
end

puts  BasicObject.eigen                  # => #<Class:BasicObject>
puts  BasicObject.eigen.ancestors.to_s   # => [Class, Module, Object, Kernel, BasicObject]


为了区分普通类和Eigenclass,Ruby会使用“#"表明该类是一个Eigenclass。从上面的代码的结果可以看出:
* 一个实例对象的Eigenclass的父类是该对象的类
* 一个类对象的Eigenclass的父类是该类对象的父类的EigenClass。
* BasicObject对象的Eigenclass的祖先链最后会回到BasicObject本身。



对象模型的大一统
本系列写到今天,涉及到了无数的对象模型概念,比方说,实例,类,模块,Eigenclass,还有实例方法,实例变量,当前对象self,当前类,单件方法,类宏等等。这些初看起来非常复杂的概念,当深入进去之后,就会发现,复杂性慢慢褪去。一切都变得简单,清晰起来,如果把Eigenclass、类和模块归结为一个东西的话(因为它们本质上的概念差不多,姑且统称为模块),Ruby的对象模型可以总结为一下几条规则:
  • 关于对象,只有2种对象,要么是实例对象,要么是模块对象,用于存放实例变量的绑定。
  • 关于模块,它可以是Eigenclass,类,或模块。用于存放方法和一些类实例变量。
  • 关于方法,方法必须存在于一种模块中。
  • 每个对象(包括模块对象)都有自己的Eigenclass,用于保存当前对象的单件方法(类对象的就是类宏)。
  • 除了BasicObjec类无超类以外,所有的模块对象都有且只有一个父类,即从任何模块对象只有一条向上直到BasicObject的祖先链。
  • 一个实例对象的Eigenclass的父类是该实例对象的类,一个模块对象的eigenclass的超类是该模块对象的超类的eigenclass。
  • 在类对象中插入一个模块时,该模块会出现在该类的祖先链的正上方。
  • 调用方法时,Ruby总是先向“右”迈一步进入接收者真正的类中,然后向上进入祖先链。






  • 大小: 1.4 MB
  • 大小: 1.2 MB
1
1
分享到:
评论
1 楼 HeLiang7 2013-05-15  
算是元编程的总结,要点都讲到了。

相关推荐

    Ruby 面向对象设计实践--2013年

    《Ruby面向对象设计实践》是一本关于如何在Ruby语言中实现面向对象编程(Object-Oriented Programming, OOP)的经典著作。该书首次出版于2013年,作者是Sandi Metz,一位著名的软件工程师和培训师,在软件开发领域...

    Ruby-Her一个ORM对象关系映射将REST资源映射成Ruby对象

    Ruby是一种动态、面向对象的编程语言,而Her是Ruby社区中的一款ORM(对象关系映射)库,专门用于处理RESTful API。ORM允许开发者用面向对象的方式来操作数据库,而无需直接编写SQL语句,简化了数据操作的过程。Her库...

    Ruby 面向对象知识总结

    **Ruby**是一种完全面向对象的编程语言,这意味着在Ruby中,一切事物都是对象。无论是字符串、数字、布尔值甚至是`true`和`false`这样的基本数据类型,都是作为对象处理的。类本身也是对象,是`Class`类的一个实例。...

    ruby面向对象设计 Practical Object-Oriented Design in Ruby

    《Ruby面向对象设计》一书由Pearson Education出版社发行,该出版社是全球教育行业的领导者之一,提供各种教育和学习资源。书籍的版权声明中提及了版权保护、商标声明、使用许可等方面的内容。这显示了出版社对于...

    Ruby-Ruby的面向对象的枚举

    在Ruby编程语言中,"面向对象的枚举"是一个重要的概念,它结合了面向对象编程的灵活性和枚举类型的效率。Ruby的枚举通常指的是枚举类(Enum Class),这是一种自定义枚举类型的方法,允许我们创建具有特定行为的枚举...

    面向对象的脚本语言Ruby

    面向对象的脚本语言Ruby

    Ruby-Reform能够给你一个Form对象模型包含验证和嵌套设置

    总的来说,Ruby-Reform是Ruby开发中的一个强大工具,能够极大地改善你的表单处理体验。它提供了更清晰的代码结构,易于测试的表单逻辑,以及对嵌套数据的优雅处理。如果你在Ruby项目中经常处理复杂的表单,那么...

    Ruby-MongoMapper针对Mongo的一个Ruby对象映射器

    MongoMapper是Ruby社区中用于MongoDB数据库的一种对象关系映射(ORM)框架,它为开发者提供了与MongoDB交互的简洁而强大的接口。MongoDB是一种非关系型数据库(NoSQL),以其灵活性、高可用性和高性能而受到青睐。...

    ruby多模型绑定

    根据给定文件中的标题“ruby多模型绑定”及其描述、标签和部分内容,我们可以总结出以下相关的知识点: ## Ruby on Rails 多模型绑定 ### 1. 多模型绑定概念介绍 在 Ruby on Rails(以下简称 Rails)框架中,多...

    Ruby-FastJSONAPI一个用于Ruby对象的快速JSONAPI的序列化器

    它旨在帮助开发者以简洁、高效的方式将Ruby对象转换为符合JSON:API规范的JSON格式,极大地提高了开发效率和应用程序的性能。 JSON:API是一个广泛接受的标准,它定义了如何在客户端和服务器之间交换数据,包括资源的...

    Ruby-wisper一个微型库为Ruby对象提供发布订阅功能

    Ruby Wisper库是一个轻量级的解决方案,它为Ruby对象引入了发布-订阅(Publish-Subscribe,简称Pub/Sub)模式。这种模式允许对象之间通过事件进行通信,而不是直接调用方法,从而增强了代码的解耦性和可扩展性。在...

    Ruby-Kashmir是一个RubyDSL使得序列化和缓存对象易如反掌

    Ruby-Kashmir是一个专门为Ruby开发者设计的Domain Specific Language(DSL),它的主要目标是简化对象的序列化和缓存过程。通过这个库,开发者可以方便地管理和存储他们的数据,提高应用程序的性能,尤其是在处理...

    《Ruby Programming—向Ruby之父学程序设计(第2版)》电子书

    Ruby编程语言是由Yukihiro Matsumoto(又称为Matz)创建的一种面向对象的语言,它以其简洁、优雅的语法和强大的功能而闻名。《Ruby Programming—向Ruby之父学程序设计(第2版)》这本书旨在帮助初学者,甚至是完全...

    Ruby.Programming_向Ruby之父学程序设计(第2版)

    《Ruby Programming:向Ruby之父学程序设计(第2版)》是为了让完全没有程序设计经验的读者也能灵活地使用Ruby,因此书中详细地说明了各种知识。从程序所需要的变量、常数、方法、类、控制结构等语法的说明,到类的主要...

    Ruby基础语法+Ruby变量与数据类型+Ruby控制结构+Ruby函数与方法+Ruby面向对象编程等全套教程

    Ruby基础语法 ...Ruby面向对象编程 Ruby模块与包 Ruby错误处理 Ruby文件与I/O操作 Ruby正则表达式 Ruby网络编程 Ruby数据库交互 Ruby测试框架 RubyWeb框架Rails入门 Ruby高级特性 Ruby性能优化与最佳实践

    Ruby Hack Guide中文版.chm

    第一部分的内容包括对Ruby语言一个概要介绍和对Ruby对象模型的讲解。从我个人阅读的感觉来看,第一章对于Ruby语言的介绍是一个非常好的起步教程,把Ruby语言中一些核心点都指了出来。比起我读到过一些Ruby语言教程,...

    Ruby-Ancestry将ActiveRecord模型组织成一个树状结构

    Ruby-Ancestry是一个非常有用的库,它允许开发者在ActiveRecord模型中构建和管理层次结构,形成树状结构。这个库特别适用于那些需要处理有层级关系数据的场景,比如分类、菜单系统、组织架构等。Ancestry通过提供一...

    Practical Object Oriented Design in Ruby 新版 实战ruby面向对象设计

    《Practical Object-Oriented Design in Ruby 新版 实战ruby面向对象设计》是一本非常受欢迎的编程书籍,它不仅是学习Ruby语言的重要参考资料,也是深入理解面向对象编程(OOP)技术不可或缺的宝贵资料。这本书由于...

Global site tag (gtag.js) - Google Analytics