[:item, :small_power_plant,
[:provides, [:electricity, 11]],
[:depends_on, :secure_air_vent]]]
可變參數方法
有些語言支持可變參數方法,此時在函數調用中使用字面量列表是一種很有用的技術。在下面的代碼中,我把這種方式應用于uses方法。
下載 lairs/rules24.rb
item :secure_air_vent
item(:acid_bath) do
uses(acid(:type => :hcl, :grade => 5),
electricity(12))
end
item(:camera) do
uses(electricity(1))
end
item(:small_power_plant) do
provides(electricity(11))
depends_on(:secure_air_vent)
end
在這種需要把方法調用中的列表組合在一起的情況下,使用可變參數是非常趁手的——尤其是當語言對在何處放置字面量列表限制得非常嚴格時。
3.7 動態接收
動態編程語言的一個特性是:它能對沒有在接收對象里定義的方法調用進行響應。
讓我們在這個例子中探索一下這句話的含義。到目前為止,我們假設巢穴里的資源數量是相對固定的,我們可以編寫相應的代碼來處理這些固定數量的資源。但如果不是假設的這種情況呢?如果有很多資源呢?如果需要把非常多的資源置于配置中呢?
下載 lairs/rules23.rb
resource :electricity, :power
resource :acid, :type, :grade
item :secure_air_vent
item(:acid_bath).
uses(acid(:type => :hcl, :grade => 5)).
uses(electricity(:power => 12))
item(:camera).
uses(electricity(:power => 1))
item(:small_power_plant).
provides(electricity(:power => 11)).
depends_on(:secure_air_vent)
electricity和acid仍舊是生成器中的方法。我希望這些方法可以創建新定義的資源,但我不希望去定義這些方法,而是希望它們能夠根據資源的數據來自動創建。
在Ruby中可以通過重寫method_missing方法來實現。在Ruby中,如果一個對象接收了一個沒有定義的方法調用,那么它就會執行method_missing方法。這個方法默認是從Object類中繼承而來,而且會拋出一個異常。我們可以通過重寫這個方法來做一些有趣的事情。
首先做好對資源方法調用的準備工作。
下載 lairs/builder23.rb
def resource name, *attributes
attributes << :name
new_resource = Struct.new(*attributes)
@configuration.add_resource_type name, new_resource
end
Ruby有一個用來創建匿名類的工具,叫做struct。當需要一個資源時,調用struct進行定義。以resource方法的第一個參數作為新定義資源的名字,并根據隨后的參數設置新定義資源的屬性(property)。然后把這些新定義的資源保存于配置中。
接著我重寫了method_missing方法。該方法在一個字面量字典中遍歷了所有新定義的資源,以確定方法名是否與新的struct之一對應。如果有,則加載這個struct。
下載 lairs/builder23.rb
def method_missing sym, *args
super sym, *args unless @configuration.resource_names.include? sym
obj = @configuration.resource_type(sym).new
obj[:name] = sym
args[0].each_pair do |key, value|
obj[key] = value
end
return obj
end
在method_missing被調用時,首先確認是否有資源可以響應這個調用。如果沒有,則調用超類中的method_missing以引發一個異常。
大多數動態語言都可以重寫“處理未知調用的方法”。這是一個很強大的技術,但在使用時需要格外小心,因為它會改變程序方法分派系統的機制。如果沒有合理使用,代碼會變得難以理解。
Ruby的生成器庫(由Jim Weirich編寫)就是一個如何正確使用method_missing的好例子。生成器庫是用來生成XML標記的,它非常合理地使用了閉包和method_missing。
可以通過一個簡單的例子來說明這一點。如下代碼,
下載 lairs/frags
Builder::XmlMarkup.new("" , 2)
puts builder.person do |b|
b.name("jim")
b.phone("555-1234" , "local" =>"yes")
b.address("Cincinnati")
end
會生成下面這段標記。
下載 lairs/frags
jim
555-1234
Cincinnati
3.8 總結
兩年之前,Dave Thomas在他的博客中提及“代碼拆招”(code katas)的概念:在嘗試使用各種方法來解決一個簡單問題的過程中,研究和比較各種解決方案的優劣。本章就是這樣的一個練習。最后我也沒有給出任何確定的結論,但它確實帶著我們探討和領略了用Ruby編寫內部DSL的各種方法(當然,大部分方法也可以通過其他語言來實現)。
原文轉自:http://www.ituring.com.cn/article/17818