[转]Ruby之类的真相
打开类和猴子补丁
在Ruby中,类定义的方法和其他的语句没有任何区别,都是一行一行的执行下去的。如下例子:
- class Example
- def method_1
- puts "method 1"
- end
- end
- class Example
- def method_2
- puts "method 2"
- end
- end
本例中,当第一次定义Class Example的时候,还没有一个叫做Example的Class存在,因此,Ruby开始定义这个类,当后面在定义这个类时,Ruby会发现该类已存在,并返回这个类,而不是定义一个新类。
因为这个特性,因此,Ruby天生具有打开一个已经存在的类,并动态修改其内容的能力,即使其是标准类库的类也不例外。比方说,可以给SDK的String类添加一个去除String中的标点符号和特殊字符的方法:to_alphanumeric
- class String
- def to_alphanumeric
- gsub /[^\w\s]/, ''
- end
- end
- “H&&^^ello”.to_alphanumeric #==>Hello
,然后,所有的String对象都具备“to_alphanumeric”的能力了,这种技术一般简称为打开类技术。
上面描述的打开类技术其实是隐含了一定的风险的,尤其是在大型系统中使用打开类技术扩展标准类库时,因为,很多开发人员都在扩展类,当多个扩展方法的名字一样时, 后定义的总会覆盖掉前面,从而导致整个系统的崩溃,业界把这种鲁莽的修改类的方式简称为猴子补丁(Monkey Patch)。因此在使用打开类技术时,一定要慎之又慎。
类的真相
实例变量
在Ruby中,实例变量是存储在对象中,但是,其于该对象的类没有关系,当给对象的实例变量赋值时,该实例变量就生成了,说白了,实例变量就像是一个挂载在对象上的HashMap,每个对象都可以用自己不同的HashMap, 如下例:
- class Person
- def name
- @name = "xianlinbox"
- end
- end
- p = Person.new
- puts p.instance_variables #==>nil
- p.name
- puts p.instance_variables #==>@name
方法
作为一个对象,除了有实例变量(也可以称之为属性),还需要有方法。 但是在Ruby中,关于方法的定义并不在对象中,而是在对象自身的类中,这是因为“共享同一个类的对象也必须共享同样的方法”。但是,不能说Class有一个叫做“method”的方法,因为无法使用"Class.method"调用该方法,而要说Class有一个实例方法“method”,这意味着必须创建该类的实例对象,通过实例对象调用该方法。
如果要定义类方法,那么在定义方法的时候,必须加类名前缀,如下:
- class Person
- def Person.name
- @name = "xianlinbox"
- end
- end
类本身也是对象
在Ruby中Class本身也是一个对象,关于对象的所有规则都适用于Class.
- puts "hello".class #=> String
- puts String.class #=> Class
- puts Class.class #=> Class
- puts Class.instance_methods(false) #=> [:superclass,:allocate,:new]
- puts Class.instance_variables #=> nil
类的继承体系
- puts String.superclass #=> Object
- puts Class.superclass #=> Module
- puts Module.superclass #=> Object
- puts Object.superclass #=> BasicObjec
- puts BasicObject.superclass #=> nil
- BasicObject是继承体系的根节点。
- 所有类都继承自Object。
- Class是对Module的继承增强,增加了new()和allocate()方法以创建实例。