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