初探 Ruby Metaprogramming
51CTO推薦專題:Ruby On Rails開(kāi)發(fā)教程
Classes are open
我們先看一段代碼:
- class String
- def say_hello
- p "Hello!"
- end
- end
- "Fred".say_hello
這里我們看到我們r(jià)eopen了String這個(gè)build-in的class,而且添加了一個(gè)新的方法say_hello(.NET 3.5中通過(guò)擴(kuò)展方法也實(shí)現(xiàn)了這個(gè)特性,但ruby的實(shí)現(xiàn)更加自然和靈活)這樣使得ruby語(yǔ)言自身提供了很大的可擴(kuò)展性,而這種從編程語(yǔ)言層面提供的可擴(kuò)展性為好處體現(xiàn)在兩個(gè)方面。
第一,對(duì)于ruby語(yǔ)言自身,在其以后的版本中可以對(duì)原有類在不破壞原有代碼的基礎(chǔ)之上提供更多更好的方法。.NET 3.5 已經(jīng)通過(guò)擴(kuò)展方法這個(gè)新特性,在原有集合類的方法之外增加了一些新的查詢方法。
第二,對(duì)于ruby的使用者,也就是我們這些ruby程序員來(lái)說(shuō)。classes are open,這就意味我們可以更加實(shí)現(xiàn)我們一些具體的特殊的需求。例如,我們希望我們應(yīng)用的程序中的String都可以提供一個(gè)encrype的方法,來(lái)實(shí)現(xiàn)加密。又或者我們對(duì)于String類的to_s方法的實(shí)現(xiàn)覺(jué)得不夠滿意,我們都可以reopen String這個(gè)類,然后定義我們的方法。因?yàn)閞uby的方法查找遵循
”Define a method twice inside the same class, the second method definition takes precedence“
所有我們毋需擔(dān)心,我們對(duì)于to_s的調(diào)用出問(wèn)題。
前面我說(shuō)道,ruby的open class比.NET提供的擴(kuò)展方法更加靈活。而這個(gè)靈活體現(xiàn)在我們可以針對(duì)一個(gè)instance去增加方法,如下
- <SPAN style="FONT-FAMILY: 黑體">fred = 'fred'
- def fred.say_hello
- p 'hello'
- end
- fred.say_hello
- </SPAN>
這樣就滿足了我們對(duì)于一些特殊instance的需求。
Definition are active
- class Logger
- if ENV['debug']
- def log
- 'debug'
- end
- else
- def log
- 'non-debug'
- end
- end
- end
這是一段非常簡(jiǎn)單的代碼,但是我們可以看到我們是否定義debug這個(gè)ENV對(duì)于我們的程序會(huì)有完全不一樣的行為。這里也許有人會(huì)說(shuō)靜態(tài)語(yǔ)言的條件編譯同樣能完成這樣的任務(wù)。那么我們就再看一段代碼
- <SPAN style="FONT-FAMILY: 黑體">result = class Fred
- puts 'Hello'
- x = 3
- end
- puts result
- </SPAN>
執(zhí)行這段代碼,我們會(huì)看到這樣的輸出結(jié)果:
Hello 3
為什么會(huì)輸出Hello呢?因?yàn)閐efinition are active,也就是定義本身就是一段可執(zhí)行的代碼。為什么會(huì)輸出3呢?因?yàn)閞uby中所有的可執(zhí)行代碼都會(huì)有返回值。到這里肯定會(huì)有人問(wèn),那么class定義中的method呢?你可以試試在irb中定義一個(gè)method,你會(huì)發(fā)現(xiàn)在irb會(huì)返回一個(gè)nil給你。
但是definition are active在我們實(shí)際開(kāi)發(fā)中有什么用呢?那讓我們看一下一個(gè)rails的應(yīng)用
- module ActiveRecord
- class Base
- def has_many models
- end
- def belongs_to model
- end
- end
- end
- class Order < ActiveRecord::Base
- has_many :items
- end
- class Item < ActiveRecord::Base
- belongs_to :order
- end
你能想想如果definition aren't activity, 還會(huì)有這樣優(yōu)雅的代碼嗎?
All methods have a receiver
在ruby中,方法的調(diào)用是以message的形式發(fā)送給相應(yīng)的instance的。比如說(shuō)foo.hello(),就是發(fā)送hello這個(gè)message給foo。這里很多人會(huì)好奇,那么如果我在irb上直接定義方法呢?其實(shí)ruby里面有一個(gè)概念叫top level execution, 它是一個(gè)Object的instance叫做main。當(dāng)你直接在irb中定義一個(gè)方法或者執(zhí)行一個(gè)方法(例如puts "hello"),同樣你只是發(fā)送了一個(gè)message,而這個(gè)message的receiver就是top level execution。
ruby代碼的執(zhí)行是與當(dāng)前代碼所在context相關(guān),不同的context關(guān)聯(lián)不同的receiver。也就是當(dāng)你的代碼在不同的context下執(zhí)行,由于context關(guān)聯(lián)的receiver不同也就有了不同的結(jié)果。
- class Context
- def name
- "smith"
- end
- p name
- def hi
- p name
- end
- end
- Context.new.hi
結(jié)果為:
"Context" "smith"
如果你想知道在你當(dāng)前context下你方法的receiver,可以通過(guò)在當(dāng)前context下調(diào)用self來(lái)獲得。
Class are Object
我們都知道一個(gè)object有什么樣的行為和屬性是在ruby中由它的class決定。比如
- class Person
- attr_reader :name
- def initialize(name)
- @name = name
- end
- def introduce
- "I'm #{@name}."
- end
- end
- p = Person.new "Dave"
對(duì)于這個(gè)例子中,p具有什么樣的行為和屬性是由Person這個(gè)class決定的??墒俏覀兛吹綄?duì)于Person我們調(diào)用了一個(gè)new的方法,那么這個(gè)new方法是由誰(shuí)定義的呢?很簡(jiǎn)單啊,我們知道p的行為和屬性由它的class也就是Person決定,那么Person的new方法應(yīng)該也來(lái)自它的class。也就是引出了Class對(duì)象,Class對(duì)象中有兩個(gè)new方法,一個(gè)是class method另一個(gè)是instance method。我們的Person.new自然調(diào)用的就是Class對(duì)象中叫new的instance method, 那么那個(gè)叫做new的class method有什么用呢?
- Person = Class.new do
- attr_reader :name
- def initialize(name)
- @name = name
- end
- def introduce
- "I'm #{@name}."
- end
- end
這段代碼可以實(shí)現(xiàn)之前那段代碼一摸一樣的功能,而這里調(diào)用的就是Class中叫做new的class method。最奇怪的Class的superclass是Module,而Module的superclass是Object,但是Class的class是自身,Module的class是Class,而Object的class也是Class(superclass是Class的方法,class是Object的方法),我們也可以說(shuō)ruby中所有的Object的class都是Class(nil的class是NilClass,但是NilClass的class是Class)。Class間接繼承Object,但是Object的class又是Class,一個(gè)典型“雞生蛋,蛋生雞”的問(wèn)題。這個(gè)問(wèn)題給我最大困惑則是:如果我調(diào)用一個(gè)對(duì)象例如上面例子中p的XX方法,而這個(gè)XX方法并沒(méi)有直接在Person中定義,那么這個(gè)XX方法是來(lái)自Class還是Object呢?而對(duì)于這一點(diǎn)ruby的解決辦法是在方法的查找receiver的時(shí)候,會(huì)先檢查Person有沒(méi)有這個(gè)XX方法,會(huì)先檢查Class后檢查Object,也就是先檢查一個(gè)class的class,然后檢查superclass。
原文鏈接:http://www.cnblogs.com/feihe/archive/2011/04/17/1951274.html
【編輯推薦】