本章内容理解:讲了Ruby对象模型,并且介绍了几种依赖于此模型的技术。
在Java和C#等语言中,直到你创建了该类的一个对象,然后调用对象的方法才会有实际的工作。
在Ruby中,类的定义有所不同。当使用class关键字时,并非是在指定对象未来的行为方式,相反,实际上是在运行代码。
Ruby对象模型介绍以及七条规则:
1.只有一种对象——要么是普通对象,要么是模块。
类就是对象,是class类的一个实例。类也是一个增强的模块,比模块多了new、allocate、superclass三个方法。
普通对象不可以使用new方法再建立一个实例,模块也是。
2.只有一种方法——它存在于一个模块中,通常是一个类中。
无论是类或模块的实例方法,类方法,都存在于类或模块中。单件方法比较特殊,但也存在于单件类中。
3.只有一种模块——可以是一个普通模块、一个类或者单件类
模块可以是普通模块,可以是一个类,即增强的模块。同时一个普通的模块无法被继承。
|
|
4.每个对象都有自己”真正的类”,要么是一个普通类,要么是一个单件类。
前面好理解,至于”要么是一个单件类”这句话,可以通过这个例子理解一下:
instance_eval方法把当前类改成了接收者s1的单件类。只有s1自己一个对象可以使用。
|
|
5.除了BasicObject,任何类只有一条向上的、直到BasicObject的祖先链。
6.一个(普通)对象(区别于类这种对象)的单件类的超类是这个对象的类,一个类的单件类的超类是这个类的超类的单件类。
|
|
7.调用一个方法时,Ruby先向右迈一步进入接受者真正的类,然后向上进入祖先链。这就是Ruby查找方法的方式。
如果它有单件类(规则4),查找会从单件类开始。
为什么Ruby要以这种方式来组织对象模型?——这样就能在子类中调用父类的类方法。
|
|
依赖Ruby对象模型而来的技术概念:
1.在不知道类名的情况下打开一个类:
|
|
这里给String类增加了一个m方法。
class_eval和class的区别:class_eval使用扁平作用域,当前的绑定依然可见。
class则打开一个新的作用域,当前的绑定不可见。
class_eval和instance_eval的区别:一般用instance_eval方法打开非类的对象,用class_eval方法打开类定义。
当前类
不管Ruby程序处于哪个位置,总存在一个当前对象:self。同样,也总有一个当前类或当前模块的存在,定义一个方法时,那个方法将成为当前类的一个实例方法。
2.类实例变量和类变量
类实例变量是Class类的实例——即继承Class的一般类的实例变量。一般类相对于Class类来说也就是个普通对象。它定义于一般类充当self的时刻,所以只能被它本身的类访问,不能被它的实例或者子类访问。
|
|
3.类宏
类宏可以为任何ruby对象创建属性。
在给对象创造属性的时候用Module#attr_ accessor(),给类创造属性时用单件方法。
属性实际上只是一对方法,如果在单件类中定义了这些方法,那么他实际上会成为类方法。
Ruby对象并没有属性。如果希望有一些像属性的东西,就得定义两个拟态方法:一个读方法和一个写方法
|
|
- 写这样的方法(也叫访问器)很快就让人感到枯燥。可以通过Module#attr_ accessor()则可以生成读写方法:
|
|
- 为普通Ruby对象创建属性:
|
|
- 为类创建属性
|
|
但这样做会让所有继承Class类的类都拥有这个属性。
|
|
所以考虑下一种做法:
- 将类属性创建在该类的单件类中
|
|
4.单件类
每个单件类只有一个实例,而且不能被继承。单件类也是一个对象单件方法的存活之所。
- 单件方法适合在什么情况下使用
仅仅针对于个别使用次数很少的对象。也就是用来增强某个类。
类方法的实质是一个类的单件方法。
类方法的实质就是:它们是一个类的单件方法。为什么要是单件方法呢?——只是这一个对象(类)能用它。
- 怎样进入单件类?
|
|
如果对象有单件类,Ruby不是从它所在的类开始查找,而是从对象的单件类开始查找方法。
在单件类中,有一种运用情况是类方法,下面来看看类方法。
5.三种定义类方法的方法:
类扩展
通过向类的单件类中增加模块来定义方法。
- 直接打开类
|
|
- include方法扩展:
|
|
- extend方法扩展:
|
|
6.方法包装器
有一个不能直接修改的方法,而我希望为这个方法包装额外的特性,这样所有的客户端都能自动获得这个额外特性。要明白方法包装器,先明白方法别名:
- 方法别名
有点绕,先看较简单的初级形式,它只输出一个字符串:
|
|
先给方法命名一个别名,然后重定义了它:
|
|
当调用real_length时,它会调用原来的length并输出结果。
调用length时,它会通过real_length与5比较,然后得到结果。同样的,调用real_length时,它会调用原来的length并输出结果。
- 定义环绕别名
1.给方法定义一个别名
2.重定义这个方法
3.在新的方法中调用老的方法
它的作用本质是往老方法中加入一些代码。它是一种方法包装器,下面来看看更多的方法包装器:
- 用细化封装器代替环绕别名
|
|
这里的只用一个super关键字调用原来的方法并与5比较得出结果。
细化封装器的作用范围只到文件末尾处,比环绕别名安全,因为环绕别名是全局性的。
不过还有一种方法包装的技术,使用module_prepend方法:
- 下包含包装器:
|
|
它也是一种局部化的方法包装器,不过一般认为它比细化包装器和环绕别名都更清晰。