Ruby之对象模型的大一统

[size=large][b]当前类[/b][/size]
不同于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


[size=large][b]实例变量[/b][/size]
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世界的静态变量了。
[size=large][b]单件方法,类宏[/b][/size]
在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中方法调用的过程,“向左一步进入该对象的类,然后沿着祖先链一直查找方法,找到方法之后,根据自身的绑定执行该方法”。
[img]http://dl.iteye.com/upload/attachment/0084/4824/78956d26-ca2a-30f0-b230-ea323c45370b.png[/img]

因此,对象本身只有一组绑定,而方法定义都是在类中。那么上面说到的单件方法和类宏应该在什么地方定义呢? 单件方法肯定不能定义在类中,否则将会影响该类的所有实例对象。类本身也是对象,类的方法不能定义在自身,因为对象的方法必须定义在对象的类中,而类对象的类是Class,如果把类方法定义到Class上,那么所有的类对象都会拥有该方法。这一切迷思的答案都来源于一个Ruby中的高级概念,Eigenclass
[size=large][b]Eigenclass[/b][/size]
在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本身。

[img]http://dl.iteye.com/upload/attachment/0084/4826/663c6b72-979b-354b-93a0-50370d416601.png[/img]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值