什么时候需要读这本书?
扫过一遍基本的 Ruby 语法,自己也写过一些 Ruby 代码,觉得 Ruby 也就是一个普通的脚本语言而已的时候。
这本书带给读者什么东西?
在 Ruby 语法背后, Ruby 语言的构建模型。以及在这种模型下,一些可能只属于 Ruby 的实际编程案例(也就是怎么样用 Ruby 的特性去简化和优美你的代码)
那么,元编程是什么?
一般来说,『元xx』这样的词语都表示『创造 xx 的一种东西』或者『xx 背后更加原始的一样类似的东西』,比如 iOS 开发都很熟悉的 OC 中的元类。元编程表示的是『写出可以编写代码的代码』。
还是抽象?举一个小例子:一个类中有二十个属性,现在你需要为这些属性分别创造对应的 set 方法,类似 setXXX 这样的形式,同时要求这些 set 方法都要判断新旧值是否一致,如果一致就直接 return。
这样的事情显然是一个体力活,如果没有好的方法,你就需要写二十个十分类似的方法,并且每个方法内部都差不多,只是在属性的名称那一部分有所区别,重复度非常高。
如果使用 Ruby 的元编程,当外部调用某个 set 方法(比如 setYYY)的时候,你的一段代码就会被执行,这段代码会动态的为当前的类创建一个 setYYY 方法,同时代码中可以截断方法名称(也就是拿到了一个 'YYY' 字符串),通过这个名称去动态获取对应属性的值(一种类似 this.get_property_by_name('YYY') 的方式),和当前 set 方法的参数进行比较,完成 set 方法的要求。整个代码可能不超过十行,并且不重复,十分优雅。
应该怎样阅读这本书?
首先我们来看看这本书每一章的大致内容:
第一章:简单介绍了 Ruby 中面向对象的模型是怎么实现的,同时讲解了当调用一个对象的方法的时候发生了什么事情。
第二章:当我们知道方法调用是怎样的一个流程以后,我们就可以考虑是否可以拦截或者修改这个过程来实现一些黑魔法。
第三章:这里话头一转,转向描述 Ruby 中的代码块,也描述了和代码块息息相关的作用域的概念。
第四章:这一章把话头又转回了类,这里将详细描述对象与类的组织模型,同时讲解了一些和类有关的元编程方法。
第五章:着重讲解 eval ,一个能够把字符串当做 Ruby 代码解释的函数。
第六章:休息一下,用一页纸讲个故事。
第七章-第八章:通过讲解 ActiveRecord 这个强大库的使用方法,将之前讲过的魔法汇总一下。
第九章:指出一些使用元编程的注意事项、经验教训。
附录:给出了一些 Ruby 中的常见事物的惯常写法,解释了什么是 DSL(领域专属语言),然后将前面讲到的魔法的例子汇总了一下。
可以看出,全书主要分两部分,第一部分主要是 Ruby 背后的模型的讲解、高级语法特性的讲解,中间穿插着一些例子。第二部分则是通过解析 ActiveRecord 这个库,将前面讲到的模型的利用来了个实际展示。
因此,最好第一遍阅读的时候着重去理解 Ruby 语言背后的模型,第二遍先看例子,自己尝试想想要实现这些例子通过这些模型还需要什么东西,然后再看实际的例子。最后的附录可以在日后忘记一些写法的时候翻一翻,帮助回忆。
这本书中印象深刻的点?
对象与类的模型和方法调用:
Ruby 中的 class 关键字和 Java 等语言的 class 在理解上很不同,Java 的 class 的作用是在全局创建一个类,并且这个类在全局只能有这一个声明;Ruby 中 class 作用是把代码的上下文变换到这个类中,也就是『打开类』,同一个类可以在任何地方被打开,也因此别人的类可以被自己随意打开并改写。
Ruby 中的实例变量也不像 OC 或者 Java 那样必须提前声明,当第一次用到实例变量的时候,这个实例变量才会生成。所以在方法里头看到对一个实例变量赋值的时候有可能是在初始化这个实例变量。
对象中不存储方法,只存储属性。对一个对象调用方法的时候,会首先去这个对象的 singlenton class 中找,然后去 singleton class 的父类中(实例对象的 singleton class 的父类是这个实例对象的类,类的 singleton class 的父类是这个类的父类的 singleton class ,类也是对象,不过特殊在类这个对象只有一个)。当调用方法的时候,首先去这个对象的 singleton class 中查找,没有的话继续向 singleton class 的父类查找,在如下完整的对象与类的结构图中可以简单记成『向右一步,然后向上』。
定义类方法其实就是向类的 singleton class 中添加新的方法。
除了使用点语法调用方法,还可以使用 send 方法来调用方法,这样就可以使用字符串拼出来的方法名来调用方法了。(通过 send 甚至可以在外部调用类的私有方法)
可以使用 define_method 来创建方法,和 def 的区别在于,define_method 后面可以是一个变量。
method_missing 这个方法也可以动态增加方法。注意用这种方式的时候也要重写 respond_to? 方法。
模块有一个 included 的类方法,在这个模块被 include 的调用,我们可以在里头写上自动增加类方法的代码,这样一个模块被 include 了以后,不止增加了实例方法,还增加了类方法,这个叫做类扩展混入(Class Extension Mixin)
同样一个 @a ,声明在类的作用域中和类的方法的作用域中是不一样的。前者是类对象的实例变量,后者的类的实例对象的实例变量。
Block: 与 作用域
任何方法都可以传入一个 block ,不过有的方法会不处理 block ,此时传入 block 什么用也没有。
一个作用域可以理解为一个上下文环境,在同一个作用域中的代码可以互相访问彼此的变量。当有 class/def/module 的时候,代码会进入一个新的作用域,并且作用域之间并没有什么关系,也就是说内部作用域不会得到外部作用域的变量(除了全局变量,全局变量是可以在任何作用域中得到的)。
可以使用 block 利用闭包在不同作用域之间传递变量。
block 都是待着作用域的,也就是说 block 这个『行为』带着这个行为创建时的『数据』。
通过 method(:method_name) 可以得到一个 method 对象,不过 method 对象必须绑定到一个对象上才能被调用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。