一個巢穴,二十種Ruby DSL(6)

發表于:2013-02-28來源:圖靈社區作者:Martin Fowler點擊數: 標簽:
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

  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

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97