第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Ruby 入門教程 Ruby 入門教程 44 使用 Ruby 編寫 DSL 語言

使用 Ruby 編寫 DSL 語言

領(lǐng)域特定語言(英語:domain-specific language、DSL)指的是專注于某個(gè)應(yīng)用程序領(lǐng)域的計(jì)算機(jī)語言。又譯作領(lǐng)域?qū)S谜Z言。同名著作是 DSL 領(lǐng)域的豐碑之作,由世界級(jí)軟件開發(fā)大師和軟件開發(fā)“教父” Martin Fowler 歷時(shí)多年寫作。

Ruby 中很多的框架都采用了 DSL 語言的風(fēng)格,比如:Grape 和 Rspec。今天讓我們學(xué)習(xí)使用 Ruby 的語言來寫一下 DSL。

1. 編寫第一個(gè) DSL 語言

現(xiàn)在經(jīng)理給我們提了一個(gè)需求:讓我們監(jiān)聽?zhēng)讉€(gè)數(shù)據(jù):用戶成功創(chuàng)建訂單數(shù)、用戶成功付款訂單數(shù)、商家及時(shí)放貨的訂單數(shù)、等等幾十個(gè)事件,每天更新一次,后臺(tái)管理員可以從后臺(tái)看到這些數(shù)據(jù)。

我們理想中的 DSL 代碼格式應(yīng)該是這樣的:

listen "用戶成功創(chuàng)建訂單數(shù):" do
  # 從數(shù)據(jù)庫獲取用戶今天創(chuàng)建的訂單數(shù)
  # order = ...
  # order.count
end

代碼塊(Block)中返回需要顯示的數(shù)量。

由此我們可以寫出這個(gè)代碼。

實(shí)例:

def listen description
  puts "#{description}#{yield}" if yield
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  300
end

listen "用戶成功付款訂單數(shù):" do
  150
end

listen "商家及時(shí)放貨的訂單數(shù):" do
  130
end

# ---- 輸出結(jié)果 ----
用戶成功創(chuàng)建訂單數(shù):300
用戶成功付款訂單數(shù):150
商家及時(shí)放貨的訂單數(shù):130

2. DSL中使用變量

2.1 定義常規(guī)變量

如果我們現(xiàn)在要在 DSL 中插入變量應(yīng)該怎們辦呢,比如增加一個(gè)必須大于 150 才通知的限制。在上一章節(jié)的作用域中我們可以學(xué)到,變量是可以在閉包外定義作用于閉包內(nèi)的,所以我們可以這樣的改動(dòng)代碼。

實(shí)例:

limit = 150

listen "用戶成功創(chuàng)建訂單數(shù):" do
  order_count = 300
  order_count > limit ? order_count: nil
end

listen "用戶成功付款訂單數(shù):" do
  order_count = 150
  order_count > limit ? order_count: nil
end

listen "商家及時(shí)放貨的訂單數(shù):" do
  order_count = 130
  order_count > limit ? order_count: nil
end

# ---- 輸出結(jié)果 ----
用戶成功創(chuàng)建訂單數(shù):300

2.2 變量集中初始化

但是如果我們需要增加很多種變量,這樣定義變量的方式會(huì)讓人感覺散亂又無序,一般DSL的語法會(huì)讓我們定義變量在一個(gè)專門定義變量的塊中,下面就是一個(gè)例子。

實(shí)例:

define do
  @limit = 150
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  order_count = 300
  order_count > @limit ? order_count: nil
end

listen "用戶成功付款訂單數(shù):" do
  order_count = 150
  order_count > @limit ? order_count: nil
end

listen "商家及時(shí)放貨的訂單數(shù):" do
  order_count = 130
  order_count > @limit ? order_count: nil
end

我們將上述代碼制成一個(gè) event.rb,然后在實(shí)現(xiàn)代碼中進(jìn)行引用:

實(shí)例:

@defines = []
@listens = []

def define &block
  @defines << block
end

def listen description, &block
  @listens << {description: description, condition: block}
end

load 'event.rb'

@listens.each do |listen|
  @defines.each do |define|
    define.call
  end 
  condition = listen[:condition].call
  puts "#{listen[:description]}#{condition}" if condition
end

# ---- 輸出結(jié)果 ----
用戶成功創(chuàng)建訂單數(shù):300

Tips:load 方法會(huì)加載 event.rb 并執(zhí)行文件中的代碼。

解釋:

在實(shí)例中我們將塊yield換成了&proc的形式,我們定義了兩個(gè)處于頂級(jí)作用域中的變量:@defines@listens,我們將每次從define中定義的proc對(duì)象都保存在了@defines中,將所有監(jiān)聽的事件也都保存到了@listens中,在后面的代碼里面,每一次我們處理listen事件的時(shí)候都會(huì)運(yùn)行define,這樣就完成了變量集中初始化。

2.3 消除事件之間共享頂級(jí)作用域變量

2.3.1 潔凈室

上述處理方法中,不同事件頂級(jí)實(shí)例變量會(huì)存在一個(gè)共享的問題,處理這個(gè)問題之前,首先讓我了解一下潔凈室(Clean Room)的概念。

潔凈室是一個(gè)用來執(zhí)行塊的環(huán)境。理想的潔凈室是不應(yīng)該有任何的方法以及實(shí)例變量的,所以不會(huì)產(chǎn)生任何方法名或者變量名的沖突。因此BasicObjectObject往往被用來充當(dāng)潔凈室。

實(shí)例:

obj = Object.new
obj.instance_eval do
  @a = 1
  @b = 2
  @c = 3
end

obj.instance_eval do 
  puts "@a == #{@a}"
  puts "@b == #{@b}"
  puts "@c == #{@c}"
  puts "sum == #{@a + @b + @c}"
end

# ---- 輸出結(jié)果 ----
@a == 1
@b == 2
@c == 3
sum == 6

解釋:

讓我們創(chuàng)建一個(gè)Object的實(shí)例作為潔凈室,使用instance_eval在潔凈室第一個(gè)塊中里面定義三個(gè)變量,在第二個(gè)塊中定義4個(gè)方法。因?yàn)樗麄兊淖饔糜蚴沁@個(gè)潔凈室的實(shí)例,所以會(huì)得到最后的輸出結(jié)果。

2.3.2 用潔凈室來處理上述問題

讓我們使用潔凈室處理剛剛頂級(jí)作用域的問題。

先修改一下event.rb。

define do
  @limit = 150 
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  @num = 1 
  puts "@num1 == #{@num}"
  order_count = 300 
  order_count > @limit ? order_count: nil 
end

listen "用戶成功付款訂單數(shù):" do
  @num = @num.to_i + 1
  puts "@num2 == #{@num}"
  order_count = 150 
  order_count > @limit ? order_count: nil 
end

listen "商家及時(shí)放貨的訂單數(shù):" do
  @num = @num.to_i + 1
  puts "@num3 == #{@num}"
  order_count = 130 
  order_count > @limit ? order_count: nil 
end

重新運(yùn)行一下腳本,得到結(jié)果:

@num1 == 1
用戶成功創(chuàng)建訂單數(shù):300
@num2 == 2
@num3 == 3

每一個(gè)事件之間應(yīng)該是獨(dú)立的,不應(yīng)該共享不必要的變量,為此,我們使用潔凈室修改一下實(shí)現(xiàn)代碼。

實(shí)例:

@defines = []
@listens = []

def define &block
  @defines << block
end

def listen description, &block
  @listens << {description: description, condition: block}
end

load 'event.rb'

@listens.each do |listen|
  env = Object.new
  @defines.each do |define|
    env.instance_eval &define
  end 
  condition = env.instance_eval &listen[:condition]
  puts "#{listen[:description]}#{condition}" if condition
end

# ---- 輸出結(jié)果 ----
@num1 == 1
用戶成功創(chuàng)建訂單數(shù):300
@num2 == 1
@num3 == 1

解釋:

我們?cè)谥暗幕A(chǔ)上,每一次定義事件的時(shí)候創(chuàng)建了一個(gè)潔凈室,這樣實(shí)例變量的作用范圍就從頂級(jí)作用域變?yōu)榱藵崈羰覍?duì)象之中,事件之間就不會(huì)存在共享變量的情況了。

3. 小結(jié)

本章節(jié)中我們學(xué)習(xí)到了如何使用 Ruby 去寫一個(gè) DSL 語言,了解了 DSL 語言中使用不同變量的方法,學(xué)習(xí)了潔凈室的使用方法。