Ruby元编程
現(xiàn)在關(guān)于Ruby元編程可以說比較熱門,這個(gè)隱藏在Ruby背后的特性隨著大家對Ruby的了解逐漸顯現(xiàn)出來啦。這篇文章是自己對Ruby MetPrgamming的理解。
元編程中的元是指元信息(Meta),主要是為其載體提供基本信息,如html頁面中就有meta,如content type說明,SEO等。在C++,Java和Ruby語言中,也有元信息概念,如加載到內(nèi)存中(運(yùn)行期)的當(dāng)個(gè)對象模型(Class或者Object),都會包含元信息(藍(lán)色)和執(zhí)行代碼(灰色):
圖中藍(lán)色區(qū)塊表示元信息,C++最少,在C++編譯的過程中,編譯器僅保留了很少的元信息;Java次之,包含了部分元信息;Ruby在解析過程中保留很多信息,所以元信息最多。當(dāng)然這里不能說C++設(shè)計(jì)不合理,C++流行的時(shí)候,那個(gè)時(shí)候內(nèi)存說不定是640K的,硬盤16M的,如果保留過多的信息,那勢必導(dǎo)致程序無法運(yùn)行,這些設(shè)計(jì)都是出于性能的考慮。當(dāng)然Java在后續(xù)的版本(Java 9,Java 10?)中可能會添加更多的元信息,都不確定,畢竟語言在發(fā)展。
就一個(gè)Class來說,這些元信息是什么,如Java,元信息區(qū)塊包括編譯版本號、類名、屬性、函數(shù)方法名、返回值、參數(shù)類型、annotation等等,這個(gè)我們可以通過查看Java Class文件得知。對于Ruby來說,元信息則更豐富,如對函數(shù)來說,函數(shù)的參數(shù)名都會保存下來。接下來還有一個(gè)關(guān)于元信息的是否可以修改的問題,C++和Java中,這些元信息都是只讀的,你不能更改。一個(gè)例子,我們在做Java Debug的時(shí)候,如果我們修改函數(shù)內(nèi)部的邏輯,那么hotspot機(jī)制可以重新加載該class,但是你給Java Class添加屬性或函數(shù)后,hotspot機(jī)制會警告你無法進(jìn)行新的class加載,這個(gè)時(shí)候我們需要重新啟動(dòng)應(yīng)用進(jìn)行debug。當(dāng)然也不是絕對的,JavaRebel能夠在修改Java Class結(jié)構(gòu)后,仍能重新加載,其主要是其修改了Java對象模型,不在這里討論。但是Ruby就不一樣拉,元信息不是封閉的,是可以動(dòng)態(tài)修改的,這樣就可以在運(yùn)行期調(diào)整其結(jié)構(gòu)。綜合上述的討論,Ruby的元信息豐富,且可以動(dòng)態(tài)修改,所以我們可以就元信息進(jìn)行更多的編程,實(shí)現(xiàn)某些特殊的功能,而C++和Java在這方面就表現(xiàn)的比較弱一些。
講述了元信息,那么什么是元編程?就是利用元信息進(jìn)行編碼,也就是你的代碼中包含對元信息的訪問和修改。網(wǎng)上有對元編程的定義為”Write code to write code”(利用代碼去寫代碼),可能不便于理解,如你編寫Maven Plugin或者Velocity模板來生成代碼,這些都不是元編程,因?yàn)樗鼈儔焊蜎]有接觸元信息,就是代碼生成工具而已。在Java中,如果你的代碼訪問了元信息(java.lang.reflect下的類),我們就說你使用了反射機(jī)制,反射其實(shí)就是Java中的元編程,所以這里將代碼中涉及元信息的操作定義為元編程。在代碼中如何訪問元信息?其實(shí)很簡單,有相關(guān)的API,如Java中,java.lang.reflect下的類都是圍繞元信息設(shè)計(jì)的。在Ruby中,和Java一樣,有相關(guān)的函數(shù),如instance_methods、class_eval, instance_eval, define_method等等,這些函數(shù)主要負(fù)責(zé)訪問和修改元信息,網(wǎng)上有很多文章介紹這些函數(shù)的,不會很難理解。
為何要進(jìn)行元編程?我們寫代碼主要是為了實(shí)現(xiàn)業(yè)務(wù)邏輯,如數(shù)據(jù)庫操作,調(diào)用搜索引擎等,而元編程只是在操作元信息,好像和業(yè)務(wù)邏輯無關(guān),其實(shí)不然。我們訪問和修改元信息主要目的有兩個(gè):簡化代碼和實(shí)現(xiàn)復(fù)雜邏輯。簡化代碼雖然不直接為業(yè)務(wù)邏輯服務(wù),但是會降低維護(hù)成本,如動(dòng)態(tài)函數(shù)定義等都是這樣的目的;實(shí)現(xiàn)復(fù)雜邏輯,舉一個(gè)例子,如Java中,我們利用反射機(jī)制來實(shí)現(xiàn)AOP,這樣一些復(fù)雜的功能(事務(wù)、監(jiān)控統(tǒng)計(jì)、Cache、權(quán)限控制等)就非常容易實(shí)現(xiàn)。在Ruby中,使用元編程實(shí)現(xiàn)的ActiveRecord讓復(fù)雜的數(shù)據(jù)庫操作更加簡單,利用Ruby元編程可以實(shí)現(xiàn)各種DSL,如Sinatra就是HTTP的DSL。
我們進(jìn)行元編程的主要目的是解決業(yè)務(wù)邏輯實(shí)現(xiàn)中的技術(shù)問題,所以元編程時(shí)有一定的適用場合,不是說利用元編程實(shí)現(xiàn)某種奇淫技巧,造成代碼理解困難,增加維護(hù)成本,那就不必要啦。元編程也有一些局限性:
1 動(dòng)態(tài)結(jié)構(gòu)更改可能導(dǎo)致某些不可預(yù)料的錯(cuò)誤,如程序A按照既定的邏輯編程,而B程序使用元編程,更改了相關(guān)的元信息,導(dǎo)致邏輯不正確,這些都是有的;
2 元編程屬于高級技術(shù),不是一般人能理解,而且不是面向業(yè)務(wù)的,使用不好反而會增加維護(hù)成本;
3 對IDE工具不友好,IDE工具對強(qiáng)類型語言,如Java支持非常好。如果你在Ruby中動(dòng)態(tài)定義一些函數(shù)(如使用class_eval),那么IDE將無法完成這些動(dòng)態(tài)定義的函數(shù)提示,開發(fā)效率也會降低的,不過不少IDE(如RubyMine)已經(jīng)開始著手解決這些問題。
最后說明一下,元編程并不是某種語言的專利,其實(shí)每種語言都包含的這樣的特性,只不過支持的程度不一樣。Ruby設(shè)計(jì)的對象模型讓元編程更容易實(shí)現(xiàn),而且非常容易實(shí)現(xiàn)一些復(fù)雜功能,所以這也成了Ruby的特點(diǎn),這些特點(diǎn)在Rails的代碼中體現(xiàn)的尤為明顯,甚至到了極致的地步,所以也成就了Rails這款非常優(yōu)秀的框架。
總結(jié)
- 上一篇: 几种方法来实现scp拷贝时无需输入密码
- 下一篇: Boost库实现线程池学习及线程实现的异