最近因工作需要学习Ruby,被Ruby的优雅和灵活所吸引,阅读《Ruby元编程》的时候更是酣畅淋漓,想吃了炫迈一样,第五章类定义是本书中的核心部分,在这里记录下相本章相关笔记。

类定义

类定义

定义类不止是定义方法。其实,可以在类定义中放入任何代码

1
2
3
4
5
class MyClass
puts "Hello"
end

< Hello

就像方法和块一样,类定义也会返回最后一条语句的值

1
2
3
4
5
result = class MyClass
self
end

result # => MyClass

定义类(或模块)时,类本身充当当前对象self的角色。类和模块也是对象,所以类也可以充当self。

当前类
  • Ruby解释器总是追踪当前类(或模块)的引用。所有使用def定义的方法成为当前类的实例方法。

  • 在类定义中,当前类就是self——正在定义的类

  • 可以使用class_eval(或module_eval)方法打开一个已存在的类的引用

1
2
3
4
5
6
7
8
def add_method_to(a_class)
a_class.class_eval do
def m; 'Hello!'; end
end

add_method_to String

"abc".m

instance_eval和class_eval方法的选择:通常使用instalce_eval/class_eval方法打开非类的对象;用class_eval方法打开类的定义,然后用def定义方法。如果打开的对象也是类(或模块),只修改self而且并不关心它是不是一个类,那么instance_eval就很好;如果使用打开类技巧修改类,使用class_eval方法更好。

类实例变量

类的实例变量不同于类的对象的实例变量。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyClass
# 这个my_var 定义于MyClass充当self的时刻,它是MyClass的实例变量,也就是类实例变量。
@my_var = 1
# 同样类实例变量
def self.read; @my_var; end

# 这个my_var 定义于obj充当self的时刻,它是obj的实例变量
def write; @my_var = 2; end
def read; @my_var; end
end

obj = MyClass.new
obj.read # => nil
obj.write
obj.read # => 2
MyClass.read # => 1

单件方法

使用单件方法

Ruby允许给单个对象增加一个方法。

1
2
3
4
5
6
7
8
9
str = "just a regular string"

def str.title?
self.upcase == self
end

str.title? # => fasle
str.methods.grep(/title?/) # => [:title?]
str.singleton_methods # => [:title?]

只对单个对象生效的方法称为单件方法(Singleton Method)。可以直接 def obj.singleton_method来定义单件方法,也可以使用Object#define_singleton_method方法来定义。

类方法的真相

类也是对象,而类名只是常量。

1
2
3
4
5
# 由变量引用的对象上调用方法
an_object.a_method

# 一个由常量引用的对象(也是一个类)上调用方法。和上面语法完全一样
AClass.a_class_method

类方法的实质上是一个类的单件方法

类宏

Ruby对象没有属性。如果要实现类似属性的东西,需要定义两个方法。不过Module类中定义了三个访问器:attr_readerattr_writerattr_accessor,像这样的方法称为类宏(Class Macro)。类宏看起来很像关键字,但是它们只是普通的方法,只不过可以用在类定义里。

单件类

Ruby查找方法时先向右一步进入接收者的类,然后在向上查找,那么单件方法存放在哪里呢?

向对象询问它的类时,并非直接到实例它的类,而是一个对象特有的隐藏类。这个类称为该对象的单件类

Object#class会把单件类隐藏起来。Ruby有一种特殊的基于class的关键字的语法,可以进入该单件类的作用域:

1
2
3
class << an_object
# ......
end

还可以通过Object#singleton_class方法来获得单件类的引用:

1
"abc".singleton_class 	# => #<Class:#<String:0x331df0>>

单件类也是类,是特殊的类。用Object#singleton_class方法或class<< obj语法获得它。同时,每个单件类只有一个实例,而且不能被继承。单件类是一个对象的单件方法的存活的地方

Ruby对象模型的七条规则

  1. 只有一种对象——要么是普通对象,要么是模块。
  2. 只有一种模块——可以是一个普通模块、一个类或者一个单件类。
  3. 只用一种方法,它存在于一个模块中——通常是一个类中。
  4. 每个对象(包括类)都有自己“真正的类”——要么是一个普通类,要么是一个单件类。
  5. 除了BasicObject类没有超类外,每个类有且只有一个祖先——要么是一个类,要么是一个模块。这意味着任何类只有一条向上的、直到BasicObject的祖先链。
  6. 一个对象的单件类的超类是这个对象的类;一个类的单件类的超类是这个类的超类的单件类。
  7. 调用一个方法时,Ruby先向右一步进入接收者真正的类,然后向上进入祖先链,这就是Ruby查找方法的方式。