Ruby元编程读书笔记一
Open Classes
定义一个函数,去掉字符串中的标点符号和特殊字符,只保留字母、数字和空格:
def to_alphanumeric(s)
s.gsub /[^\w\s]/, ''
end
用更加面向对象的方法,应该是打开 String 类,植入 to_alphanumeric() 方法:
class String
def to_alphanumeric
gsub /[^\w\s]/, '' # 此处省略了self
end
end
Inside Class Defintions
在 Ruby 中,定义类的语句和其他语句没有本质区别,可以在类定义中放置任何语句:
3.times do
class C
puts "Hello"
end
end
像执行其他代码一样,Ruby 执行了这些在类中定义的代码,注意这里并不是定义三个同名的类。
Ruby 的 class 关键字更像是一个作用域操作符而不是类型声明语句。它的确可以创建一个还不存在的类,不过也可以把这看成是一种副作用。对于 class 关键字,其核心任务是把你带到类的上下文中,让你可以在其中定义方法。
我们总是可以重新打开已经存在的类并对它进行动态修改,即使是像 String 或 Array 这样标准库中的类也不例外。这种技术,被称为 打开类 技术。打开类技术的隐患在于,如果粗心地为某个类添加了某些方法,可能会无意中覆盖原有的某些方法,这就是 猴子补丁 这种说法的由来。
The Truth About Classes
class MyClass
def my_method
@v = 1
end
end
obj = MyClass.new
obj.class #=> MyClass
obj.instance_variables #=> []
obj.my_method
obj.instance_variables #=> [:@v]
obj.methods.grep(/my/) #=> [:my_method]
在 Ruby 中,对象的类和它的实例变量没有关系,当给实例变量赋值时,它们就生成了。因此,对同一个类,你可以创建具有不同实例变量的对象。如果这里不曾调用 obj.my_method() 方法,那么 obj 对象根本不会有任何实例变量。当然,除了实例变量,对象还有实例方法,这里调用 Object#methods 方法,可以获得一个对象的方法列表。绝大多数对象都从 Object 类中继承了一组方法,使用 Array#grep 方法展示了 my_method 方法确实在于 obj 对象列表中。
-
实际上,obj 这个对象其实并没有真正存放一组方法,其内部仅仅包含它的实例变量以及一个对自身类的引用。
-
实例变量存放在对象中,而方法存放在类中,所以同一个类的对象共享同样的方法,但不共享实例变量。
Class Revisited
-
类自身也是对象,适用于对象的规则也适用于类。类和其他任何对象一样,也有自己的类,它的名字叫做 Class 。
-
类也有方法,一个对象的方法也是其类的实例方法,所以一个类的方法也就是 Class 的实例方法。
Class.instance_methods(false) #=> [:superclass, :allocate, :new]
new 方法用来创建对象,allocate 方法是 new 方法的支撑方法, superclass 方法返回一个类的超类。
String.superclass #=> Object
Object.superclass #=> BasicObject
BasicObject.superclass #=> nil
所有的类最终都继承于 Object , Object 本身又继承于 BasicObject,BasicObject 是Ruby 对象体系的根节点。
Class.superclass #=> Module
Module.superclass #=> Object
因此,一个类只不过是一个增强的 Moudle ,增加了三个方法—new, allocate 和 superclass 而已。这几个方法可以让你创建对象并可以把它们纳入到类体系架构中。除此之外,类和模块基本上是一样的。
正如普通的对象那样,也是通过引用来访问类,obj 和 MyClass 都是引用,唯一的区别在于,obj 是一个变量,而 MyClass 是一个常量。就像类是对象一样,类名也无非就是常量。
一个模块基本上就是一组实例方法,而类是一个增加了若干新功能(一个 superclass 方法和一个 new 方法)的模块。同时存在模块和类的主要原因在于 清晰性。通常,希望它应该在别处被包含(include)时,或者被当成命名空间时,应该选择使用模块;当希望它被实例化或者继承时,应该选择使用类。