天天看點

Ruby元程式設計

現在關于Ruby元程式設計可以說比較熱門,這個隐藏在Ruby背後的特性随着大家對Ruby的了解逐漸顯現出來啦。這篇文章是自己對Ruby MetPrgamming的了解。

元程式設計中的元是指元資訊(Meta),主要是為其載體提供基本資訊,如html頁面中就有meta,如content type說明,SEO等。在C++,Java和Ruby語言中,也有元資訊概念,如加載到記憶體中(運作期)的當個對象模型(Class或者Object),都會包含元資訊(藍色)和執行代碼(灰色):

Ruby元程式設計

圖中藍色區塊表示元資訊,C++最少,在C++編譯的過程中,編譯器僅保留了很少的元資訊;Java次之,包含了部分元資訊;Ruby在解析過程中保留很多資訊,是以元資訊最多。當然這裡不能說C++設計不合理,C++流行的時候,那個時候記憶體說不定是640K的,硬碟16M的,如果保留過多的資訊,那勢必導緻程式無法運作,這些設計都是出于性能的考慮。當然Java在後續的版本(Java 9,Java 10?)中可能會添加更多的元資訊,都不确定,畢竟語言在發展。

就一個Class來說,這些元資訊是什麼,如Java,元資訊區塊包括編譯版本号、類名、屬性、函數方法名、傳回值、參數類型、annotation等等,這個我們可以通過檢視Java Class檔案得知。對于Ruby來說,元資訊則更豐富,如對函數來說,函數的參數名都會儲存下來。接下來還有一個關于元資訊的是否可以修改的問題,C++和Java中,這些元資訊都是隻讀的,你不能更改。一個例子,我們在做Java Debug的時候,如果我們修改函數内部的邏輯,那麼hotspot機制可以重新加載該class,但是你給Java Class添加屬性或函數後,hotspot機制會警告你無法進行新的class加載,這個時候我們需要重新啟動應用進行debug。當然也不是絕對的,JavaRebel能夠在修改Java Class結構後,仍能重新加載,其主要是其修改了Java對象模型,不在這裡讨論。但是Ruby就不一樣拉,元資訊不是封閉的,是可以動态修改的,這樣就可以在運作期調整其結構。綜合上述的讨論,Ruby的元資訊豐富,且可以動态修改,是以我們可以就元資訊進行更多的程式設計,實作某些特殊的功能,而C++和Java在這方面就表現的比較弱一些。

講述了元資訊,那麼什麼是元程式設計?就是利用元資訊進行編碼,也就是你的代碼中包含對元資訊的通路和修改。網上有對元程式設計的定義為”Write code to write code”(利用代碼去寫代碼),可能不便于了解,如你編寫Maven Plugin或者Velocity模闆來生成代碼,這些都不是元程式設計,因為它們壓根就沒有接觸元資訊,就是代碼生成工具而已。在Java中,如果你的代碼通路了元資訊(java.lang.reflect下的類),我們就說你使用了反射機制,反射其實就是Java中的元程式設計,是以這裡将代碼中涉及元資訊的操作定義為元程式設計。在代碼中如何通路元資訊?其實很簡單,有相關的API,如Java中,java.lang.reflect下的類都是圍繞元資訊設計的。在Ruby中,和Java一樣,有相關的函數,如instance_methods、class_eval, instance_eval, define_method等等,這些函數主要負責通路和修改元資訊,網上有很多文章介紹這些函數的,不會很難了解。

為何要進行元程式設計?我們寫代碼主要是為了實作業務邏輯,如資料庫操作,調用搜尋引擎等,而元程式設計隻是在操作元資訊,好像和業務邏輯無關,其實不然。我們通路和修改元資訊主要目的有兩個:簡化代碼和實作複雜邏輯。簡化代碼雖然不直接為業務邏輯服務,但是會降低維護成本,如動态函數定義等都是這樣的目的;實作複雜邏輯,舉一個例子,如Java中,我們利用反射機制來實作AOP,這樣一些複雜的功能(事務、監控統計、Cache、權限控制等)就非常容易實作。在Ruby中,使用元程式設計實作的ActiveRecord讓複雜的資料庫操作更加簡單,利用Ruby元程式設計可以實作各種DSL,如Sinatra就是HTTP的DSL。

我們進行元程式設計的主要目的是解決業務邏輯實作中的技術問題,是以元程式設計時有一定的适用場合,不是說利用元程式設計實作某種奇淫技巧,造成代碼了解困難,增加維護成本,那就不必要啦。元程式設計也有一些局限性:

1 動态結構更改可能導緻某些不可預料的錯誤,如程式A按照既定的邏輯程式設計,而B程式使用元程式設計,更改了相關的元資訊,導緻邏輯不正确,這些都是有的;