Chinaunix首页 | 论坛 | 博客
  • 博客访问: 396595
  • 博文数量: 69
  • 博客积分: 1984
  • 博客等级: 上尉
  • 技术积分: 953
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-28 00:43
个人简介

学无所长,一事无成

文章分类

全部博文(69)

文章存档

2015年(19)

2014年(14)

2013年(9)

2012年(17)

2010年(10)

我的朋友

分类: Python/Ruby

2012-07-11 00:20:55

1.2 打开类技术

类可以直接用 class 打开,因此你可以任意往现有类中添加方法(注意有可能覆盖同名方法),当然这不是一种好的风格,但你有这种能力,在某些情况下你可以获得特殊的效果。


  1. class String
  2.     def to_alphanumeric
  3.         gsub /[^\w\s]/,’’
  4.     end
  5. end


 

class 关键字更像是一个作用域操作符,而非类型声明语句。class确实可以创建一个还不存在的类,不过这更像是一种副作用,其核心任务还是把你带到类的上下文当中。

 

打开类所带来的问题

如果你为某个已知类添加方法,可能会将该类的原有方法覆盖。这种鲁莽的方式令人不悦,我们一般管这种行为叫猴子补丁 Monkey patch。由于类可以在任何地方打开,因此猴子补丁在代码庞大时可能难以追踪。一般我们可以通过命名空间来限定其影响范围。

1.3 类的真相

对象中有什么

1、实例变量:Ruby 中对象的类和它的实例变量没有关系。对同一个类,你可以创建具有不同实例变量的对象。

2、方法:一个对象仅仅包含它的实例变量以及一个对自身类的引用。方法存在于类当中,而非对象中。

对象的方法实际上是其类的实例方法,如:


  1. String.instance_methods == “abc”.methods # true,等式成立
  2. String.methods == “abc”.methods         # false ,等式不成立;注:String 有双重身份,既是类,同时也是对象。


总结:一个对象的实例变量存在于对象本身,而一个对象的方法存在于对象自身的类中。

重访类

类自身也是对象,它是 Class 类的实例对象。

  1. 'abcd'.class     # => String
  2. String.class     # => Class

一个对象的方法也是其类的实例方法。

  1. inherited = false
  2. Class.instance_methods(inherited)     # =>[:superclass,:allocate,:new]
  3. String.superclass                     # => object
  4. Object.superclass                     # => BasicObject
  5. BasicObject.superclass                 # => nil

所有类都继承于 Object ,Object 又继承于 BasicObject (这本质上就是一个白板类,用作模板)。Class 同样具有父类。


  1. Class.superclass     # => Module
  2. Module.superclass     # => Object

这里,我已经彻底困惑了,到底是先有鸡,还是先有蛋,也许学到后面就明白了。实际上,这里体现出两种关系,父类同子类的关系,对象同类的关系。

一个类不过是一个增强的 Module ,在其上增加了三个方法 – new 、allocate 、superclass 而已。除此之外,Class 同 Module 基本上一样的。绝大多数适用于 Class 的,同样也适用于 Module ,反之亦然。

就像类是对象一样,类名也无非就是常量


  1. module MyModule
  2.     MyConstant = ‘Outer constant’
  3.     class MyClass
  4.         MyConstant = ‘inner constant’
  5.     end
  6. end


这里 Module(也是常量) 还有 Class (也是常量) 可以想象成目录,其他常量可以想象成文件,因此我们可以这样引用一个常量:


  1. MyModule::MyConstant
  2. MyModule::MyClass::MyConstant


这是两个不同的常量。

就像用目录来组织文件一样,我们可以用模块来组织常量。 

来自Rake 的例子 – p17


  1. module Rake
  2.     class Task
  3.     # ......
  4.     end
  5. end


通过此方式,我们定义了一个 Rake::Task 名字空间,有效避免了命名冲突。

前面说过常量类似于文件系统中的目录和文件,那我们是不是可以创建目录和文件的链接呢(类似于 unix 下的 ln 指令),这完全可以:


  1. # Task 和 Rake::Task 表示同样的 Class 实例。是不是同 unix 指令 ln –s Rake/Task Task 很类似呢。
  2. Task= Rake::Task     

  3. # 我们就可以直接使用快捷方式了。
  4. FileTask = Task::FileTask


 

常量的路径:通过 :: 分隔,可以在常量前面加上 ::  表示根路径,如 ::Rake::Task::xxx

Module 类提供了两个角 constants() 的方法,一个是实例方法,一个类方法。Module#constants() 方法返回当前范围内的常量,有点像unix 的 ls 指令。Module.constants() 方法返回当前程序中所有顶级常量,包括类名。

这个概念理解较为困难,参考如下文章:

http://www.kuqin.com/rubycndocument/man/built-in-class/class_object_module.html#constants

如果想获得当前常量的路径,则可以使用 Module.nesting() 方法:


  1. module M
  2.     class C
  3.         module M2
  4.             Module.nesting # =>
  5.         end
  6.     end
  7. end


修剪常量树 – P19

load(‘motd.rb’) 会加载文件并运行,motd.rb 中定义的常量和类会污染当前作用域。因此可以如下

load(‘motd.rb’,true) # true ,强制加载文件中的常量仅在自身范围内有效。Ruby 会创建一个匿名模块,使用它作为命名空间。加载完成后,该模块销毁。

require 同 load 颇为相似,但它导入文件后不执行。

 

对象和类的小结:

1、对象 = 实例变量 + 指向其类的引用。对象的方法存在于类中,在类中,被称为类的实例方法。

2、类 = Class 类的一个实例对象 + 一组实例方法(即该类的实例方法) + 指向超类的引用。 Class 是 Module 的子类,因此一个类也是一个模块。

3、类有自己的方法(如 new(),即我们通常所说的类方法),这些是 Class 类的实例方法。
类名 = 常量 = 指向 Class 类实例对象的引用

比如 String 是个类名,同时也是个常量,同时也是 Class  的实例对象 (可以通过 Class.new 生成)

 

1.5 方法查找

调用 obj.func 时的步骤:obj 为接受者,func 为方法。
“向右一步,再向上”。先向右一步来到接受者所在的类,然后沿着祖先链向上直到找到给定的方法。

  1. MySubclass.ancesstors # => [MySubclass,MyClass,Object,Kernel,BasicObject]
注意这里的 kernel。
如果在一个类中包含 (include)了一个模块,Ruby 会创建一个封装该模块的匿名类,并将其插入祖先链中,其位置正好在包含该模块的类的上访。
由上面例子可以看出,在 class Object 中必定有某处 include Kernel 。

这些匿名的用于封装的类叫做包含类、或者代理类,superclass() 方法会假装他们不存在,但通过 ancesstors 是可以看到的。

Kernel 模块

Kernel 提供了很多通用方法,如 print 等,这些方法实际上都是 Kernel 模块的私有方法。由于 class Object 中包含了 Kernel,因此 Kernel 进入祖先链,其他类从 Object 继承,因此可以随意调用 print 等方法。

注:print 等看起来像是 ruby 的关键字,其实不是,它仅仅是一个方法而已。Ruby 内一切皆对象,非常和谐统一,干的漂亮吧。

执行方法

  1. def my_method
  2.     tmp = @x + 1
  3.     my_other_method(tmp)
  4. end
当执行上述代码是,我们需要知道 @x 实例变量 属于哪个对象,我们要在哪个对象上面调用 my_other_method。答案很简单,都属于 my_method 方法的接受者。如何找到他呢?这就要用到 self 了。

探索 self

每一行代码都会在一个对象中执行,这就是当前对象,self。当调用一个方法时,接受者就成为self 。没有明确指明接受者的调用都在 self 上进行。

当开始运行 Ruby 程序时,Ruby 解释器会创建一个名为 main 的对象来作为当前对象。这个对象又被称为顶级上下文。

  1. self         #main
  2. self.class    #object

类定义于self

在类和模块的定义中,self 由这个类或模块自身担任。

  1. class MyClass
  2.     self     # MyClass
  3. end

私有 (private)究竟意味着什么

两条规则:1、调用某方法,我们要考虑是否需要指明接受者。2、私有方法不能指明接受者,只能作用于隐含的 self 上。

如果我们需要指明接受者,则能调用的方法必定不是私有方法。Ruby 实际上通过这种非常简单的机制实现了 private 方法。

1.6 这里展示了一个祖先链混乱的例子
1.7 对象模型小结

1、对象由一组实例变量和一个类的引用组成
2、对象的方法存在于对象所属的类中(从类的角度看,他们叫做实例方法)
3、类本身是 Class 类的对象。类的名字不过是一个常量而已。
4、Class类是Module的子类。一个模块基本上是由一组方法组成的包。类处理具有模块的特性以外,还可以被实例化(通过 new() 方法)及被组织为层次结构(通过他的 superclass() 方法)
5、常量像文件系统一样,是按照树形结构组织的。其中模块和类的名字扮演目录的角色,其他普通的常量则扮演文件的角色。
6、每个类都有一个祖先链,这个链从自己所属的类开始,向上直到 BasicObject 类结束。
7、当调用一个方法时,Ruby首先向右一步来到接受者所属的类,然后一直向上查找祖先链,直到找到该方法,或者到达链的顶端为止。
8、每当类包含一个模块时,该模块会被插入到祖先链中,位置在该类的正上方。
9、当调用一个方法时,接受者会扮演 self 的角色。
10、当定义一个模块(或者类)时,该模块扮演 self 的角色。
11、实例变量永远都被认定为 self 的实例变量。
12、任何没有明确指定接受者的方法调用,都当成是调用 self 的方法。


阅读(1190) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~