深入解讀Ruby DSL概念
對(duì)于一個(gè)初學(xué)Ruby語(yǔ)言的朋友來(lái)說(shuō),對(duì)于Ruby DSL的理解還不是很清晰。在這篇文章中我們將會(huì)為大家詳細(xì)介紹有關(guān)Ruby DSL的一些使用方法。#t#
DSL是一種專(zhuān)注于某一特定領(lǐng)域的語(yǔ)言,使用通用語(yǔ)言(如C或者Java)當(dāng)然可以得到與DSL相同的功能。但是這樣會(huì)產(chǎn)生大量繁瑣的代碼并導(dǎo)致大量的領(lǐng)域知識(shí)被隱藏在通用語(yǔ)言構(gòu)造中(如for循環(huán),if條件,方法調(diào)用,import聲明等等)。
生成以及維護(hù)通用語(yǔ)言編寫(xiě)的代碼本身也是問(wèn)題所在:專(zhuān)家們必須將他們的知識(shí)變?yōu)榇a。通常這些專(zhuān)家(銷(xiāo)售人員,經(jīng)理或者園?。┤鄙倬幊讨R(shí),這意味著他們必須與程序員進(jìn)行協(xié)作。當(dāng)然,這也意味對(duì)代碼的任何一次修改都包括許多繁瑣的步驟:如果一個(gè)領(lǐng)域?qū)<倚枰淖兡承┕δ埽仨毷紫扰c程序員進(jìn)行交流,由程序員實(shí)現(xiàn)這些修改,然后領(lǐng)域?qū)<覚z查這些代碼是否表現(xiàn)出期望的行為,諸如此類(lèi)。
對(duì)于上述問(wèn)題,其中一個(gè)可能的解決方案是開(kāi)發(fā)一種適應(yīng)特定環(huán)境的語(yǔ)言,非技術(shù)人員可以用它來(lái)解決問(wèn)題。這種語(yǔ)言較通用語(yǔ)言更簡(jiǎn)潔并且僅僅提供目標(biāo)領(lǐng)域所需要的類(lèi)型以及特定的語(yǔ)言構(gòu)造。
在訪(fǎng)談中,Obie將DSL類(lèi)比為自然語(yǔ)言中的俚語(yǔ)或者行話(huà)。全世界喜愛(ài)咖啡的人對(duì)下面這句話(huà)一定非常熟悉:
Venti half-caf, non-fat, no foam, no whip latte
在正常的對(duì)話(huà)中,上述語(yǔ)言不能傳遞正確的語(yǔ)義。并且世界上大多數(shù)的咖啡館里可能僅僅提供最普通的牛奶加咖啡。但是在合適的場(chǎng)所使用上述語(yǔ)言(例如,星巴克),它將讓你使用最少的詞語(yǔ)喝到想要的飲料并且很少會(huì)造成誤解。
有多種方式可以實(shí)現(xiàn)DSL。其中一個(gè)選擇是定義一種語(yǔ)法并使用解析器生成工具(如ANTLR或者YACC)來(lái)生成解析器。通過(guò)它可以將Ruby DSL代碼轉(zhuǎn)化為某種可以被解釋的數(shù)據(jù)結(jié)構(gòu)(語(yǔ)法樹(shù))。其中一個(gè)例子是Make文件,它被用來(lái)定義構(gòu)建過(guò)程(編譯,打包,部署)。
另一種方式是使用XML而不是解析器,它將對(duì)于解析器生成工具的依賴(lài)變?yōu)閷?duì)于XML解析器的依賴(lài),有許多工具可以用來(lái)支撐XML處理(使用DOM解析器可以得到類(lèi)似語(yǔ)法樹(shù)的數(shù)據(jù)結(jié)構(gòu),同時(shí)使用XPATH來(lái)提取數(shù)據(jù),等等)。Ant,是一個(gè)基于XML的DSL, 構(gòu)建過(guò)程被定義為XML格式。Make和Ant就是外部DSL最典型的實(shí)現(xiàn)。
另一個(gè)解決方案是內(nèi)部Ruby DSL,它不需要使用任何解析器生成工具以及XML解析器,取而代之的是使用現(xiàn)有的合法的通用語(yǔ)言的構(gòu)造,很明顯,這種語(yǔ)言的構(gòu)造必須非常靈活才可以容納各種簡(jiǎn)練的DSL代碼。
內(nèi)部DSL的***解決工具非LISP以及類(lèi)LISP語(yǔ)言莫屬,原因是LISP靈活的語(yǔ)法,它可以被概括為:
原子
幾乎任何字符都可以成為原子,如foo,:::bar:::甚至加號(hào)都是合法的原子
零個(gè)或多個(gè)原子組成的序列
放入圓括號(hào)中
LISP宏特性使得開(kāi)發(fā)者可以很輕易的在LISP中定義DSL并且將其解釋或者展開(kāi)為可以執(zhí)行的通常的LISP代碼。
另外一種適于使用內(nèi)部DSL的語(yǔ)言是Ruby, 沒(méi)什么可驚奇的,想想Ruby豐富的語(yǔ)法吧,它是Ruby得以支持內(nèi)部Ruby DSL的部分特性。下面是Obie在PPT上所使用到的觀點(diǎn)以及相應(yīng)的示例代碼:
方法調(diào)用中括號(hào)可以省略。這看起來(lái)沒(méi)什么大不了的, 但是它使得下面的聲明成為合法的Ruby代碼:
order = latte venti, half_caf, non_fat, no_foam, no_whip在上面的例子中,latte(拿鐵咖啡)以及我們所需要的特別的口味兒都是方法調(diào)用的一部分。latte方法返回一個(gè)將咖啡各個(gè)屬性進(jìn)行了初始化的對(duì)象。
類(lèi)的定義在載入時(shí)可以被動(dòng)態(tài)執(zhí)行
class RuleSet < ActiveRecord::Base has_many :commends, :dependend => :delete_all # ... more... end這是一個(gè)內(nèi)部DSL在Rails的ActiveRecord中的例子。has_many調(diào)用會(huì)在類(lèi)***次被載入時(shí)執(zhí)行。這個(gè)調(diào)用被用來(lái)設(shè)定類(lèi)的關(guān)聯(lián)以及行為,例如,它可以通過(guò)define_method調(diào)用向類(lèi)添加一些方法。這樣這段Ruby DSL代碼的用戶(hù)可以使用非常簡(jiǎn)潔,描述性的方式來(lái)定義類(lèi)某些方面的行為。事實(shí)上,這與LISP宏有很多相似的地方,他們都在代碼被載入時(shí)進(jìn)行工作。
簡(jiǎn)潔的代碼塊書(shū)寫(xiě)格式(Block)
塊是自包含的Ruby代碼。它可以被保存,當(dāng)作參數(shù)傳遞并在以后執(zhí)行,這個(gè)概念也被稱(chēng)做是匿名方法,lamda演算或者閉包(Closures),在Ruby中向方法傳遞塊非常簡(jiǎn)潔緊湊,它使得實(shí)現(xiàn)特定語(yǔ)言構(gòu)造變得非常容易,Rake是與Make和Ant非常相似的構(gòu)建工具,我們來(lái)看一個(gè)例子:
task :default => [:test] task :test do ruby "test/unittest.rb" end上面的類(lèi)使用了我們?cè)谶@里提到的所有的三種概念。task是一種方法調(diào)用,但是沒(méi)有圓括號(hào)讓它變的更具描述性。task調(diào)用在載入時(shí)被執(zhí)行,對(duì)內(nèi)部數(shù)據(jù)結(jié)構(gòu)進(jìn)行設(shè)置。test任務(wù)的邏輯在塊中進(jìn)行定義(在do和end之間的代碼),并在適當(dāng)?shù)臅r(shí)候被執(zhí)行。
在Ruby中,內(nèi)部DSL使得編寫(xiě)簡(jiǎn)潔和描述性的規(guī)范變得非常容易。正如我們從has_many例子中看到的,內(nèi)部Ruby DSL也可以很容易的與通常命令式的Ruby代碼進(jìn)行混合。















 
 
 
 
 
 
 