item :secure_air_vent
item :acid_bath,
:uses => [acid(:type => :hcl,
:grade => 5) ,
electricity(12)]
item :camera,
:uses => electricity(1)
item :small_power_plant,
:provides => electricity(11),
:depends_on => :secure_air_vent
使用這種方法有利有弊。對于像小型電廠這種簡單物件它非常合適。但對于如酸性溶液這種復雜物件,它卻不合適。酸性溶液依賴于兩種資源,所以需要把對acid和electricity的調用放在一個列表中。一旦把它們置于字面量映射中,代碼就變得很不直觀。
接下來實現會變得更加復雜。對item方法的調用既需要名稱,也需要映射。在Ruby中會將其視作一個name參數后面跟著一個“名稱—值”對形式的多重參數。
下載 lairs/builder4.rb
def item name, *args
newItem = Item.new name
process_item_args(newItem, args) unless args.empty?
@config.add_item newItem
return self
end
process_item_args函數根據不同的鍵來切換處理每個閉包,要注意的是:args中的值可能是一個元素,也可能是一個列表。
下載 lairs/builder4.rb
def process_item_args anItem, args
args[0].each_pair do |key, value|
case key
when :depends_on
oneOrMany(value) {|i| anItem.add_dependency(@config[i])}
when :uses
oneOrMany(value) {|r| anItem.add_usage r}
when :provides
oneOrMany(value) {|i| anItem.add_provision i}
end
end
end
def oneOrMany(obj, &block)
if obj.kind_of? Array
obj.each(&block)
else
yield obj
end
end
當你遇到這種情況——傳入的參數值既可能是一個單獨的元素,也可能是一個列表——時,始終把參數作為列表傳入通常會讓情況變得比較簡單。
下載 lairs/rules21.rb
item :secure_air_vent
item :acid_bath,
[:uses,
acid(:type => :hcl, :grade => 5),
electricity(12)]
item :camera,
[:uses, electricity(1)]
item :small_power_plant
[:provides, electricity(11)],
[:depends_on, :secure_air_vent]
上面代碼中item方法的參數是一個名稱和一個列表(而不是散列)。列表中的第一個元素是鍵,其后的元素是這個鍵對應的值(這就是Lisp程序員用列表模擬散列的方法)。這種方法減少了嵌套層次,并且更易處理。
下載 lairs/builder21.rb
def item name, *args
newItem = Item.new name
process_item_args(newItem, args) unless args.empty?
@config.add_item newItem
return self
end
def process_item_args anItem, args
args.each do |e|
case e.head
when :depends_on
e.tail.each {|i| anItem.add_dependency(@config[i])}
when :uses
e.tail.each {|r| anItem.add_usage r}
when :provides
e.tail.each {|i| anItem.add_provision i}
end
end
end
在這里需要注意的是,我們把列表當作了一個頭和尾的組合(而不是一系列元素)來處理。所以不要用只有兩個元素的列表來替換散列,因為那沒有任何價值。在這里我們用第一個元素為鍵,其他元素為值的列表替換散列,這樣我們就不需要在一個集合中嵌套另一個集合。
頭和尾并非是Ruby的列表(叫做Array)默認具有的方法,但添加它們非常簡單。
下載 lairs/builder21.rb
class Array
def tail
self[1..-1]
end
alias head firsts
end
在結束字面量集合的討論之前,讓我們來看一看最后版本。下面就是以使用映射為主,列表為輔的整個配置代碼。
下載 lairs/rules22.rb
{:items => [
{:id => :secure_air_vent},
{:id => :acid_bath,
:uses => [
[:acid, {:type => :hcl, :grade => 5}],
[:electricity, 12]]},
{:id => :camera,
:uses => [:electricity, 1]},
{:id => :small_power_plant,
:provides => [:electricity, 11],
:depends_on => :secure_air_vent}
]}
下面是只使用列表實現的版本,也叫Greenspun版本1。
1 出自Philip Greenspun的“第十編程法則”:任何使用靜態類型檢查語言編寫的、足夠復雜的程序都包含一個特定、非正式定義、容易引入Bug且緩慢的動態檢查語言實現。——譯者注
下載 lairs/rules6.rb
[
[:item, :secure_air_vent],
[:item, :acid_bath,
[:uses,
[:acid,
[:type, :hcl],
[:grade, 5]],
[:electricity, 12]]],
[:item, :camera,
[:uses, [:electricity, 1]]],
原文轉自:http://www.ituring.com.cn/article/17818