第一章节的内容是对象模型,相对来说比较简单,本篇文章先直接总结知识点,其实以后再回过头来看本篇文章的时候直接看知识点总结就可以了,除了知识点的罗列之外,重点介绍几个关键性的知识,比如self的使用,方法查找、方法执行、Kernel模块中定义的方法、私有方法调用的方式等。
下面是这个章节的知识点总结:
- 对象由一组实例变量和一个类的引用组成
- 对象的方法存在于对象所属的类中,从类的角度看,它们叫做实例方法
- 类本身是Class类的对象,类的名字不过是一个常量而已
- Class类是Module的子类,一个模块基本上是由一组方法组成的包,类除了具有模块的特性之外,还可以被实例化(new)以及被组织为层次结构(superclass)
- 常量像文件系统一样,是按照树形结构组织的,其中模块和类的名字扮演目录的角色,其他普通的常量则扮演文件的角色
- 每个类都有一个祖先链,这个链从自己所属的类开始,向上直到BasicObject类结束
- 当调用一个方法时,Ruby首先向右一步到接受者所属的类,然后一直向上查找祖先链,直到找到该方法,或者到达链的顶端为止
- 每当类包含一个模块时,该模块会被插入到祖先链中,位置在该类的正上方
- 当调用一个方法时,接收者会扮演self的角色
- 当定义一个模块或者类时,该模块扮演self的角色
- 实例变量永远被认定为self的实例变量
- 任何没有明确指定接受者的方法调用,都当成是调用self的方法。
self的使用
如知识点9、10所示,self的角色通常由最后一个接收到方法调用的对象来充当,不过在类和模块的定义中,并且在任何方法定义之外,self的角色由这个类或模块担任,代码如下所示:
class MyClass
puts self #打印结果是MyClass
end
当前对象显示,在类或者模块外面,当前对象是main,代码如下所示:
puts self #这段代码返回的是main
private的使用
私有(private)方法是实现一个类时使用的内部方法,它只能被这个类(或者它的子类)的实例方法所调用。私有方法只能隐式地被self对象调用,并且不能通过一个对象进行显式调用。如果m是一个私有方法,那么只能用m这种方式来调用它,而不能用obj.m或者self.m来调用它,代码如下所示:
class C
def public_method
self.private_method()
end
private
def private_method
end
end
C.new.public_method #这段代码会报出NoMethodError
方法查找
规则:向右一步,再向上,先向右一步来到接收者所在的类,然后沿着祖先链向上直到找到给定的办法,代码如下所示:
class MyClass
end
class MySubClass < MyClass
end
puts MySubClass.ancestors #这段代码打印出来的结果是[MySubClass, MyClass, Object, Kernel, BasicObject]
如果类中include模块,代码如下所示:
module Printable
end
module Document
end
class Book
include Document
include Printable
end
puts Book.ancestors #这段代码返回的是[Book, Printable, Document, Object, Kernel, BasicObject]
#上面这段代码说明最后include的模块,其方法最先被使用
#假如Document和Printable模块都包含了print方法,Book类的实例调用print方法,最先调用的是模块Printable中的方法
以上的代码说明,后加入的模块刚好在Book类的下面。
方法执行
下面的代码是演示代码执行:
class MyClass
def testing_self
my_method #这段代码的作用和self.my_method一致
end
def my_method
puts "this is my_method"
end
end
obj = MyClass.new
obj.testing_self #这段代码返回的是this is my_method
上面代码中testing_self的self是obj,在方法内部中调用my_method,其self也是obj,my_method和self.my_method的作用是一样的。
Kernel方法的调用
Kernel中的方法也被称为Kernel method(内核方法),这个关键知识点也可以见我提出的一个问题,问题内容见链接,问题中的知识点总计已经整理成文章。
Refinement
修改类带来一个问题就是,这种修改是全局性的,解决这个方法的方案是使用refinement,而且只能在模块中进行refine,类中不行。
refinement只在三种场合有效:
- refine代码块内部
- 如果是模块中,从using开始到模块结束
- 如果是顶层上下文中,从using开始到文件结束
#在refine代码块内部有效的代码如下所示:
module StringExtensions
refine String do
def to_alphanumeric
gsub(/[^\w\s]/, '')
end
"**first**".to_alphanumeric #=>first
end
end
在全局中使用refinement
module StringExtensions
refine String do
def to_alphanumeric
gsub(/[^\w\s]/, '')
end
end
end
"my *first* refinement!".to_alphanumeric #undefined method
using StringExtensions
"my *first* refinement!".to_alphanumeric #=>my first refinement
在模块中使用refinement
module StringExtensions
refine String do
def reverse
"cc"
end
end
end
module StringStuff
using StringExtensions
"my_string".reverse #=>"cc"
end
"my_string".reverse #=> "gnirts_ym"
测试
1、利用ancestors方法理解类的继承和模块导入原则
2、利用refine方法介绍refinement的三种形式