什么時候需要讀這本書?
掃過一遍基本的 Ruby 語法,自己也寫過一些 Ruby 代碼,覺得 Ruby 也就是一個普通的腳本語言而已的時候。
這本書帶給讀者什么東西?
在 Ruby 語法背后, Ruby 語言的構建模型。以及在這種模型下,一些可能只屬于 Ruby 的實際編程案例(也就是怎么樣用 Ruby 的特性去簡化和優(yōu)美你的代碼)
那么,元編程是什么?
一般來說,『元xx』這樣的詞語都表示『創(chuàng)造 xx 的一種東西』或者『xx 背后更加原始的一樣類似的東西』,比如 iOS 開發(fā)都很熟悉的 OC 中的元類。元編程表示的是『寫出可以編寫代碼的代碼』。
還是抽象?舉一個小例子:一個類中有二十個屬性,現(xiàn)在你需要為這些屬性分別創(chuàng)造對應的 set 方法,類似 setXXX 這樣的形式,同時要求這些 set 方法都要判斷新舊值是否一致,如果一致就直接 return。
這樣的事情顯然是一個體力活,如果沒有好的方法,你就需要寫二十個十分類似的方法,并且每個方法內部都差不多,只是在屬性的名稱那一部分有所區(qū)別,重復度非常高。
如果使用 Ruby 的元編程,當外部調用某個 set 方法(比如 setYYY)的時候,你的一段代碼就會被執(zhí)行,這段代碼會動態(tài)的為當前的類創(chuàng)建一個 setYYY 方法,同時代碼中可以截斷方法名稱(也就是拿到了一個 'YYY' 字符串),通過這個名稱去動態(tài)獲取對應屬性的值(一種類似 this.get_property_by_name('YYY') 的方式),和當前 set 方法的參數(shù)進行比較,完成 set 方法的要求。整個代碼可能不超過十行,并且不重復,十分優(yōu)雅。
應該怎樣閱讀這本書?
首先我們來看看這本書每一章的大致內容:
- 第一章:簡單介紹了 Ruby 中面向對象的模型是怎么實現(xiàn)的,同時講解了當調用一個對象的方法的時候發(fā)生了什么事情。
- 第二章:當我們知道方法調用是怎樣的一個流程以后,我們就可以考慮是否可以攔截或者修改這個過程來實現(xiàn)一些黑魔法。
- 第三章:這里話頭一轉,轉向描述 Ruby 中的代碼塊,也描述了和代碼塊息息相關的作用域的概念。
- 第四章:這一章把話頭又轉回了類,這里將詳細描述對象與類的組織模型,同時講解了一些和類有關的元編程方法。
- 第五章:著重講解 eval ,一個能夠把字符串當做 Ruby 代碼解釋的函數(shù)。
- 第六章:休息一下,用一頁紙講個故事。
- 第七章-第八章:通過講解 ActiveRecord 這個強大庫的使用方法,將之前講過的魔法匯總一下。
- 第九章:指出一些使用元編程的注意事項、經驗教訓。
- 附錄:給出了一些 Ruby 中的常見事物的慣常寫法,解釋了什么是 DSL(領域專屬語言),然后將前面講到的魔法的例子匯總了一下。
可以看出,全書主要分兩部分,第一部分主要是 Ruby 背后的模型的講解、高級語法特性的講解,中間穿插著一些例子。第二部分則是通過解析 ActiveRecord 這個庫,將前面講到的模型的利用來了個實際展示。
因此,最好第一遍閱讀的時候著重去理解 Ruby 語言背后的模型,第二遍先看例子,自己嘗試想想要實現(xiàn)這些例子通過這些模型還需要什么東西,然后再看實際的例子。最后的附錄可以在日后忘記一些寫法的時候翻一翻,幫助回憶。
這本書中印象深刻的點?
對象與類的模型和方法調用:
- Ruby 中的 class 關鍵字和 Java 等語言的 class 在理解上很不同,Java 的 class 的作用是在全局創(chuàng)建一個類,并且這個類在全局只能有這一個聲明;Ruby 中 class 作用是把代碼的上下文變換到這個類中,也就是『打開類』,同一個類可以在任何地方被打開,也因此別人的類可以被自己隨意打開并改寫。
- Ruby 中的實例變量也不像 OC 或者 Java 那樣必須提前聲明,當?shù)谝淮斡玫綄嵗兞康臅r候,這個實例變量才會生成。所以在方法里頭看到對一個實例變量賦值的時候有可能是在初始化這個實例變量。
-
對象中不存儲方法,只存儲屬性。對一個對象調用方法的時候,會首先去這個對象的 singlenton class 中找,然后去 singleton class 的父類中(實例對象的 singleton class 的父類是這個實例對象的類,類的 singleton class 的父類是這個類的父類的 singleton class ,類也是對象,不過特殊在類這個對象只有一個)。當調用方法的時候,首先去這個對象的 singleton class 中查找,沒有的話繼續(xù)向 singleton class 的父類查找,在如下完整的對象與類的結構圖中可以簡單記成『向右一步,然后向上』。enter image description here
- 定義類方法其實就是向類的 singleton class 中添加新的方法。
- 除了使用點語法調用方法,還可以使用 send 方法來調用方法,這樣就可以使用字符串拼出來的方法名來調用方法了。(通過 send 甚至可以在外部調用類的私有方法)
- 可以使用 define_method 來創(chuàng)建方法,和 def 的區(qū)別在于,define_method 后面可以是一個變量。
- method_missing 這個方法也可以動態(tài)增加方法。注意用這種方式的時候也要重寫 respond_to? 方法。
- 模塊有一個 included 的類方法,在這個模塊被 include 的調用,我們可以在里頭寫上自動增加類方法的代碼,這樣一個模塊被 include 了以后,不止增加了實例方法,還增加了類方法,這個叫做類擴展混入(Class Extension Mixin)
- 同樣一個 @a ,聲明在類的作用域中和類的方法的作用域中是不一樣的。前者是類對象的實例變量,后者的類的實例對象的實例變量。
Block: 與 作用域
- 任何方法都可以傳入一個 block ,不過有的方法會不處理 block ,此時傳入 block 什么用也沒有。
- 一個作用域可以理解為一個上下文環(huán)境,在同一個作用域中的代碼可以互相訪問彼此的變量。當有 class/def/module 的時候,代碼會進入一個新的作用域,并且作用域之間并沒有什么關系,也就是說內部作用域不會得到外部作用域的變量(除了全局變量,全局變量是可以在任何作用域中得到的)。
- 可以使用 block 利用閉包在不同作用域之間傳遞變量。
- block 都是待著作用域的,也就是說 block 這個『行為』帶著這個行為創(chuàng)建時的『數(shù)據(jù)』。
- 通過 method(:method_name) 可以得到一個 method 對象,不過 method 對象必須綁定到一個對象上才能被調用。