因?yàn)?DSL 以及 DSL 的界定本身就是一個(gè)比較模糊的概念,所以難免有與他人觀點(diǎn)意見相左的地方挎狸,如果有不同的意見,我們可以具體討論。
最近在公司做了一次有關(guān) DSL 在 iOS 開發(fā)中的應(yīng)用的分享砾医,這篇文章會(huì)簡(jiǎn)單介紹這次分享的內(nèi)容。
這次文章的題目雖然是談?wù)?DSL 以及 DSL 的應(yīng)用衣厘,不過文章中主要側(cè)重點(diǎn)仍然是 DSL表谊,會(huì)簡(jiǎn)單介紹 DSL 在 iOS 開發(fā)中(CocoaPods)是如何應(yīng)用的娇斩。
沒有銀彈从祝?
1987 年腻豌,IBM 大型電腦之父 Fred Brooks 發(fā)表了一篇關(guān)于軟件工程中的論文 [No Silver Bullet—Essence and Accidents of Software Engineering](No Silver Bullet—Essence and Accidents of Software Engineering) 文中主要圍繞這么一個(gè)觀點(diǎn):沒有任何一種技術(shù)或者方法能使軟件工程的生產(chǎn)力在十年之內(nèi)提高十倍。
There is no single development, in either technology or management technique, which by itself promises even one order-of-magnitude improvement within a decade in productivity, in reliability, in simplicity.
時(shí)至今日型宙,我們暫且不談銀彈在軟件工程中是否存在(這句話在老板或者項(xiàng)目經(jīng)理要求加快項(xiàng)目進(jìn)度時(shí)撬呢,還是十分好用的),作為一個(gè)開發(fā)者也不是很關(guān)心這種抽象的理論妆兑,我們更關(guān)心的是開發(fā)效率能否有實(shí)質(zhì)的提升魂拦。
而今天要介紹的 DSL 就可以真正的提升生產(chǎn)力,減少不必要的工作箭跳,在一些領(lǐng)域幫助我們更快的實(shí)現(xiàn)需求晨另。
DSL 是什么?
筆者是在兩年以前谱姓,在大一的一次分享上聽到 DSL 這個(gè)詞的借尿,但是當(dāng)時(shí)并沒有對(duì)這個(gè)名詞有多深的理解與認(rèn)識(shí),聽過也就忘記了屉来,但是最近做的一些開源項(xiàng)目讓我重新想起了 DSL路翻,也是這次分享題目的由來。
DSL 其實(shí)是 Domain Specific Language 的縮寫茄靠,中文翻譯為領(lǐng)域特定語(yǔ)言(下簡(jiǎn)稱 DSL)茂契;而與 DSL 相對(duì)的就是 GPL,這里的 GPL 并不是我們知道的開源許可證慨绳,而是 General Purpose Language 的簡(jiǎn)稱掉冶,即通用編程語(yǔ)言真竖,也就是我們非常熟悉的 Objective-C、Java厌小、Python 以及 C 語(yǔ)言等等恢共。
Wikipedia 對(duì)于 DSL 的定義還是比較簡(jiǎn)單的:
A specialized computer language designed for a specific task.
為了解決某一類任務(wù)而專門設(shè)計(jì)的計(jì)算機(jī)語(yǔ)言。
與 GPL 相對(duì)璧亚,DSL 與傳統(tǒng)意義上的通用編程語(yǔ)言 C讨韭、Python 以及 Haskell 完全不同。通用的計(jì)算機(jī)編程語(yǔ)言是可以用來編寫任意計(jì)算機(jī)程序的癣蟋,并且能表達(dá)任何的可被計(jì)算的邏輯透硝,同時(shí)也是 圖靈完備 的。
這一小節(jié)中的 DSL 指外部 DSL疯搅,下一節(jié)中會(huì)介紹 內(nèi)部 DSL/嵌入式 DSL
但是在里所說的 DSL 并不是圖靈完備的濒生,它們的表達(dá)能力有限,只是在特定領(lǐng)域解決特定任務(wù)的秉撇。
A computer programming language of limited expressiveness focused on a particular domain.
另一個(gè)世界級(jí)軟件開發(fā)大師 Martin Fowler 對(duì)于領(lǐng)域特定語(yǔ)言的定義在筆者看來就更加具體了甜攀,DSL 通過在表達(dá)能力上做的妥協(xié)換取在某一領(lǐng)域內(nèi)的高效。
而有限的表達(dá)能力就成為了 GPL 和 DSL 之間的一條界限琐馆。
幾個(gè)栗子
最常見的 DSL 包括 Regex 以及 HTML & CSS,在這里會(huì)對(duì)這幾個(gè)例子進(jìn)行簡(jiǎn)單介紹
- Regex
-
正則表達(dá)式僅僅指定了字符串的 pattern恒序,其引擎就會(huì)根據(jù) pattern 判斷當(dāng)前字符串跟正則表達(dá)式是否匹配瘦麸。
-
- SQL
- SQL 語(yǔ)句在使用時(shí)也并沒有真正的執(zhí)行,我們輸入的 SQL 語(yǔ)句最終還要交給數(shù)據(jù)庫(kù)來進(jìn)行處理歧胁,數(shù)據(jù)庫(kù)會(huì)從 SQL 語(yǔ)句中讀取有用的信息滋饲,然后從數(shù)據(jù)庫(kù)中返回使用者期望的結(jié)果。
- HTML & CSS
- HTML 和 CSS 只是對(duì) Web 界面的結(jié)構(gòu)語(yǔ)義和樣式進(jìn)行描述喊巍,雖然它們?cè)跇?gòu)建網(wǎng)站時(shí)非常重要屠缭,但是它們并非是一種編程語(yǔ)言,正相反崭参,我們可以認(rèn)為 HTML 和 CSS 是在 Web 中的領(lǐng)域特定語(yǔ)言呵曹。
Features
上面的幾個(gè)??明顯的縮小了通用編程語(yǔ)言的概念,但是它們確實(shí)在自己領(lǐng)域表現(xiàn)地非常出色何暮,因?yàn)檫@些 DSL 就是根據(jù)某一個(gè)特定領(lǐng)域的特點(diǎn)塑造的奄喂;而通用編程語(yǔ)言相比領(lǐng)域特定語(yǔ)言,在設(shè)計(jì)時(shí)是為了解決更加抽象的問題海洼,而關(guān)注點(diǎn)并不只是在某一個(gè)領(lǐng)域跨新。
上面的幾個(gè)例子有著一些共同的特點(diǎn):
- 沒有計(jì)算和執(zhí)行的概念;
- 其本身并不需要直接表示計(jì)算坏逢;
- 使用時(shí)只需要聲明規(guī)則域帐、事實(shí)以及某些元素之間的層級(jí)和關(guān)系赘被;
雖然了解了 DSL 以及 DSL 的一些特性,但是肖揣,到目前為止帘腹,我們對(duì)于如何構(gòu)建一個(gè) DSL 仍然不是很清楚。
構(gòu)建 DSL
DSL 的構(gòu)建與編程語(yǔ)言其實(shí)比較類似许饿,想想我們?cè)谥匦聦?shí)現(xiàn)編程語(yǔ)言時(shí)阳欲,需要做那些事情;實(shí)現(xiàn)編程語(yǔ)言的過程可以簡(jiǎn)化為定義語(yǔ)法與語(yǔ)義陋率,然后實(shí)現(xiàn)編譯器或者解釋器的過程球化,而 DSL 的實(shí)現(xiàn)與它也非常類似,我們也需要對(duì) DSL 進(jìn)行語(yǔ)法與語(yǔ)義上的設(shè)計(jì)瓦糟。
總結(jié)下來筒愚,實(shí)現(xiàn) DSL 總共有這么兩個(gè)需要完成的工作:
- 設(shè)計(jì)語(yǔ)法和語(yǔ)義,定義 DSL 中的元素是什么樣的菩浙,元素代表什么意思
- 實(shí)現(xiàn) parser巢掺,對(duì) DSL 解析,最終通過解釋器來執(zhí)行
以 HTML 為例劲蜻,HTML 中所有的元素都是包含在尖括號(hào) <>
中的陆淀,尖括號(hào)中不同的元素代表了不同的標(biāo)簽,而這些標(biāo)簽會(huì)被瀏覽器解析成 DOM 樹先嬉,再經(jīng)過一系列的過程調(diào)用 Native 的圖形 API 進(jìn)行繪制轧苫。
再比如,我們使用下面這種方式對(duì)一個(gè)模型進(jìn)行定義疫蔓,實(shí)現(xiàn)一個(gè) ORM 領(lǐng)域的 DSL:
define :article do
attr :name
attr :content
attr :upvotes, :int
has_many :comments
end
在上面的 DSL 中含懊,使用 define
來定義一個(gè)新的模型,使用 attr
來為模型添加屬性衅胀,使用 has_many
建立數(shù)據(jù)模型中的一對(duì)多關(guān)系岔乔;我們可以使用 DSL 對(duì)這段“字符串”進(jìn)行解析,然后交給代碼生成器來生成代碼滚躯。
public struct Article {
public var title: String
public var content: String
public var createdAt: Date
public init(title: String, content: String, createdAt: Date)
static public func new(title: String, content: String, createdAt: Date) -> Article
static public func create(title: String, content: String, createdAt: Date) -> Article?
...
}
這里創(chuàng)建的 DSL 中的元素?cái)?shù)量非常少雏门,只有 define
attr
以及 has_many
等幾個(gè)關(guān)鍵字,但是通過這幾個(gè)關(guān)鍵字就可以完成在模型層需要表達(dá)的絕大部分語(yǔ)義哀九。
設(shè)計(jì)原則和妥協(xié)
DSL 最大的設(shè)計(jì)原則就是簡(jiǎn)單剿配,通過簡(jiǎn)化語(yǔ)言中的元素,降低使用者的負(fù)擔(dān)阅束;無論是 Regex呼胚、SQL 還是 HTML 以及 CSS,其說明文檔往往只有幾頁(yè)息裸,非常易于學(xué)習(xí)和掌握蝇更。但是沪编,由此帶來的問題就是,DSL 中缺乏抽象的概念年扩,比如:模塊化蚁廓、變量以及方法等。
抽象的概念并不是某個(gè)領(lǐng)域所關(guān)注的問題厨幻,就像 Regex 并不需要有模塊相嵌、變量以及方法等概念。
由于抽象能力的缺乏况脆,在我們的項(xiàng)目規(guī)模變得越來越大時(shí)饭宾,DSL 往往滿足不了開發(fā)者的需求;我們?nèi)匀恍枰幊陶Z(yǔ)言中的模塊化等概念對(duì) DSL 進(jìn)行補(bǔ)充格了,以此解決 DSL 并不是真正編程語(yǔ)言的問題看铆。
在當(dāng)今的 Web 前端項(xiàng)目中,我們?cè)陂_發(fā)大規(guī)模項(xiàng)目時(shí)往往不會(huì)直接手寫 CSS 文件盛末,而是會(huì)使用 Sass 或者 Less 為 CSS 帶來更強(qiáng)大的抽象能力弹惦,比如嵌套規(guī)則,變量悄但,混合以及繼承等特性棠隐。
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li { display: inline-block; }
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
也就是說,在使用 DSL 的項(xiàng)目規(guī)模逐漸變大時(shí)算墨,開發(fā)者會(huì)通過增加抽象能力的方式宵荒,對(duì)已有的 DSL 進(jìn)行拓展;但是這種擴(kuò)展往往需要重新實(shí)現(xiàn)通用編程語(yǔ)言中的特性净嘀,?所以一般情況下都是比較復(fù)雜的。
Embedded DSL(嵌入式 DSL)
那么侠讯,是否有一種其它的方法為 DSL 快速添加抽象能力呢挖藏?而這也就是這一小節(jié)的主題,嵌入式 DSL厢漩。
在上一節(jié)講到的 DSL 其實(shí)可以被稱為外部 DSL膜眠;而這里即將談到的嵌入式 DSL 也有一個(gè)別名,內(nèi)部 DSL溜嗜。
這兩者最大的區(qū)別就是宵膨,內(nèi)部 DSL 的實(shí)現(xiàn)往往是嵌入一些編程語(yǔ)言的,比如 iOS 的依賴管理組件 CocoaPods 和 Android 的主流編譯工具 Gradle炸宵,前者的實(shí)現(xiàn)是基于 Ruby 語(yǔ)言的一些特性辟躏,而后者基于 Groovy。
CocoaPods 以及其它的嵌入式 DSL 使用了宿主語(yǔ)言(host language)的抽象能力土全,并且省去了實(shí)現(xiàn)復(fù)雜語(yǔ)法分析器(Parser)的過程捎琐,并不需要重新實(shí)現(xiàn)模塊会涎、變量等特性。
嵌入式 DSL 的產(chǎn)生其實(shí)模糊了框架和 DSL 的邊界瑞凑,不過這兩者看起來也沒有什么比較明顯的區(qū)別末秃;不過,DSL 一般會(huì)使用宿主語(yǔ)言的特性進(jìn)行創(chuàng)造籽御,在設(shè)計(jì) DSL 時(shí)练慕,也不會(huì)考慮宿主語(yǔ)言中有哪些 API 以及方法,而框架一般都是對(duì)語(yǔ)言中的 API 進(jìn)行組合和再包裝技掏。
我們沒有必要爭(zhēng)論哪些是框架铃将,哪些是 DSL,因?yàn)檫@些爭(zhēng)論并沒有什么意義零截。
Rails 和 Embedded DSL
最出名也最成功的嵌入式 DSL 應(yīng)該就是 Ruby on Rails 了麸塞,雖然對(duì)于 Rails 是否是 DSL 有爭(zhēng)議,不過 Rails 為 Web 應(yīng)用的創(chuàng)建提供大量的內(nèi)置的支撐涧衙,使我們?cè)陂_發(fā) Web 應(yīng)用時(shí)變得非常容易哪工。
Ruby、 DSL 和 iOS
為了保證這篇文章的完整性弧哎,這一小節(jié)中有的一些內(nèi)容都出自上一篇文章 CocoaPods 都做了什么雁比?。
筆者同時(shí)作為 iOS 和 Rails 開發(fā)者接觸了非常多的 DSL撤嫩,而在 iOS 開發(fā)中最常見的 DSL 就是 CocoaPods 了偎捎,而這里我們以 CocoaPods 為例,介紹如何使用 Ruby 創(chuàng)造一個(gè)嵌入式 DSL序攘。
Why Ruby茴她?
看到這里有人可能會(huì)問了,為什么使用 Ruby 創(chuàng)造嵌入式 DSL程奠,而不是使用 C丈牢、Java、Python 等等語(yǔ)言呢瞄沙,這里大概有四個(gè)原因:
- 一切皆對(duì)象的特性減少了語(yǔ)言中的元素己沛,不存在基本類型、操作符距境;
- 向 Ruby 方法中傳入代碼塊非常方便申尼;
- 作為解釋執(zhí)行的語(yǔ)言,eval 模糊了數(shù)據(jù)和代碼的邊界垫桂;
- 不對(duì)代碼的格式進(jìn)行約束师幕,同時(shí)一些約定減少了代碼中的噪音。
一切皆對(duì)象
在許多語(yǔ)言伪货,比如 Java 中们衙,數(shù)字與其他的基本類型都不是對(duì)象钾怔,而在 Ruby 中所有的元素,包括基本類型都是對(duì)象蒙挑,同時(shí)也不存在運(yùn)算符的概念宗侦,所謂的 1 + 1
,其實(shí)只是 1.+(1)
的語(yǔ)法糖而已忆蚀。
得益于一切皆對(duì)象的概念矾利,在 Ruby 中,你可以向任意的對(duì)象發(fā)送 methods
消息馋袜,在運(yùn)行時(shí)自省男旗,所以筆者在每次忘記方法時(shí),都會(huì)直接用 methods
來“查閱文檔”:
2.3.1 :003 > 1.methods
=> [:%, :&, :*, :+, :-, :/, :<, :>, :^, :|, :~, :-@, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :[], :inspect, :size, :succ, :to_s, :to_f, :div, :divmod, :fdiv, :modulo, ...]
比如在這里向?qū)ο?1
調(diào)用 methods
就會(huì)返回它能響應(yīng)的所有方法欣鳖。
一切皆對(duì)象不僅減少了語(yǔ)言中類型的數(shù)量察皇,消滅了基本數(shù)據(jù)類型與對(duì)象之間的邊界;這一概念同時(shí)也簡(jiǎn)化了組成語(yǔ)言的元素泽台,這樣 Ruby 中只有對(duì)象和方法什荣,這兩個(gè)概念,極大降低了這門語(yǔ)言的復(fù)雜度:
- 使用對(duì)象存儲(chǔ)狀態(tài)
- 對(duì)象之間通過方法通信
block
Ruby 對(duì)函數(shù)式編程范式的支持是通過 block怀酷,這里的 block 和 Objective-C 中的 block 有些不同稻爬。
首先 Ruby 中的 block 也是一種對(duì)象,即 Proc
類的實(shí)例蜕依,也就是所有的 block 都是 first-class 的桅锄,可以作為參數(shù)傳遞,返回样眠。
下面的代碼演示了兩種向 Ruby 方法中傳入代碼塊的方式:
def twice(&proc)
2.times { proc.call() } if proc
end
def twice
2.times { yield } if block_given?
end
yield
會(huì)調(diào)用外部傳入的 block友瘤,block_given?
用于判斷當(dāng)前方法是否傳入了 block
。
twice do
puts "Hello"
end
twice { puts "hello" }
向 twice
方法傳入 block 也非常簡(jiǎn)單檐束,使用 do
商佑、end
或者 {
、}
就可以向任何的 Ruby 方法中傳入代碼塊厢塘。
eval
早在幾十年前的 Lisp 語(yǔ)言就有了 eval
這個(gè)方法,這個(gè)方法會(huì)將字符串當(dāng)做代碼來執(zhí)行肌幽,也就是說 eval
模糊了代碼與數(shù)據(jù)之間的邊界晚碾。
> eval "1 + 2 * 3"
=> 7
有了 eval
方法,我們就獲得了更加強(qiáng)大的動(dòng)態(tài)能力喂急,在運(yùn)行時(shí)格嘁,使用字符串來改變控制流程,執(zhí)行代碼并可以直接利用當(dāng)前語(yǔ)言的解釋器廊移;而不需要去手動(dòng)解析字符串然后執(zhí)行代碼糕簿。
格式和約定
編寫 Ruby 腳本時(shí)并不需要像 Python 一樣對(duì)代碼的格式有著嚴(yán)格的規(guī)定探入,沒有對(duì)空行、Tab 的要求懂诗,完全可以想怎么寫就怎么寫蜂嗽,這樣極大的增加了 DSL 設(shè)計(jì)的可能性。
同時(shí)殃恒,在一般情況下植旧,Ruby 在方法調(diào)用時(shí)并不需要添加括號(hào):
puts "Wello World!"
puts("Hello World!")
這樣減少了 DSL 中的噪音,能夠幫助我們更加關(guān)心語(yǔ)法以及語(yǔ)義上的設(shè)計(jì)离唐,降低了使用者出錯(cuò)的可能性病附。
最后,Ruby 中存在一種特殊的數(shù)據(jù)格式 Symbol
:
> :symbol.to_s
=> "symbol"
> "symbol".to_sym
=> :symbol
Symbol 可以通過 Ruby 中內(nèi)置的方法與字符串之間無縫轉(zhuǎn)換亥鬓。那么作為一種字符串的替代品完沪,它的使用也能夠降低使用者出錯(cuò)的成本并提升使用體驗(yàn),我們并不需要去寫兩邊加上引號(hào)的字符串嵌戈,只需要以 :
開頭就能創(chuàng)建一個(gè) Symbol 對(duì)象覆积。
Podfile 是什么
對(duì) Ruby 有了一些了解之后,我們就可以再看一下使用 CocoaPods 的工程中的 Podfile 到底是什么了:
source 'https://github.com/CocoaPods/Specs.git'
target 'Demo' do
pod 'Mantle', '~> 1.5.1'
...
end
如果不了解 iOS 開發(fā)后者沒有使用過 CocoaPods咕别,筆者在這里簡(jiǎn)單介紹一下這個(gè)文件中的一些信息技健。
source
可以看作是存儲(chǔ)依賴元信息(包括依賴的對(duì)應(yīng)的 GitHub 地址)的源地址;
target
表示需要添加依賴的工程的名字惰拱;
pod
表示依賴雌贱,Mantle
為依賴的框架,后面是版本號(hào)偿短。
上面是一個(gè)使用 Podfile 定義依賴的一個(gè)例子欣孤,不過 Podfile 對(duì)約束的描述其實(shí)是這樣的:
source('https://github.com/CocoaPods/Specs.git')
target('Demo') do
pod('Mantle', '~> 1.5.1')
...
end
Podfile 中對(duì)于約束的描述,其實(shí)都可以看作是代碼的簡(jiǎn)寫昔逗,在解析時(shí)會(huì)當(dāng)做 Ruby 代碼來執(zhí)行降传。
簡(jiǎn)單搞個(gè) Embedded DSL
使用 Ruby 實(shí)現(xiàn)嵌入式 DSL 一般需要三個(gè)步驟,這里以 CocoaPods 為例進(jìn)行簡(jiǎn)單介紹:
- 創(chuàng)建一個(gè) Podfile 中“代碼”執(zhí)行的上下文勾怒,也就是一些方法婆排;
- 讀取 Podfile 中的內(nèi)容到腳本中;
- 使用
eval
在上下文中執(zhí)行 Podfile 中的“代碼”笔链;
原理
CocoaPods 對(duì)于 DSL 的實(shí)現(xiàn)基本上就是我們創(chuàng)建一個(gè) DSL 的過程段只,定義一系列必要的方法,比如 source
鉴扫、pod
等等赞枕,創(chuàng)造一個(gè)執(zhí)行的上下文;然后去讀存儲(chǔ) DSL 的文件,并且使用 eval
執(zhí)行炕婶。
信息的傳遞一般都是通過參數(shù)來進(jìn)行的姐赡,比如:
source 'https://github.com/CocoaPods/Specs.git'
source
方法的參數(shù)就是依賴元信息 Specs
的 Git 地址,在 eval
執(zhí)行時(shí)就會(huì)被讀取到 CocoaPods 中柠掂,然后進(jìn)行分析项滑。
實(shí)現(xiàn)
下面是一個(gè)非常常見的 Podfile 內(nèi)容:
source 'http://source.git'
platform :ios, '8.0'
target 'Demo' do
pod 'AFNetworking'
pod 'SDWebImage'
pod 'Masonry'
pod "Typeset"
pod 'BlocksKit'
pod 'Mantle'
pod 'IQKeyboardManager'
pod 'IQDropDownTextField'
end
因?yàn)檫@里的 source
、platform
陪踩、target
以及 pod
都是方法杖们,所以在這里我們需要構(gòu)建一個(gè)包含上述方法的上下文:
# eval_pod.rb
$hash_value = {}
def source(url)
end
def target(target)
end
def platform(platform, version)
end
def pod(pod)
end
使用一個(gè)全局變量 hash_value
存儲(chǔ) Podfile 中指定的依賴,并且構(gòu)建了一個(gè) Podfile 解析腳本的骨架肩狂;我們先不去完善這些方法的實(shí)現(xiàn)細(xì)節(jié)摘完,先嘗試一下讀取 Podfile 中的內(nèi)容并執(zhí)行 eval
看看會(huì)不會(huì)有問題。
在 eval_pod.rb
文件的最下面加入這幾行代碼:
content = File.read './Podfile'
eval content
p $hash_value
這里讀取了 Podfile 文件中的內(nèi)容傻谁,并把其中的內(nèi)容當(dāng)做字符串執(zhí)行孝治,最后打印 hash_value
的值。
$ ruby eval_pod.rb
運(yùn)行這段 Ruby 代碼雖然并沒有什么輸出审磁,但是并沒有報(bào)出任何的錯(cuò)誤谈飒,接下來我們就可以完善這些方法了:
def source(url)
$hash_value['source'] = url
end
def target(target)
targets = $hash_value['targets']
targets = [] if targets == nil
targets << target
$hash_value['targets'] = targets
yield if block_given?
end
def platform(platform, version)
end
def pod(pod)
pods = $hash_value['pods']
pods = [] if pods == nil
pods << pod
$hash_value['pods'] = pods
end
在添加了這些方法的實(shí)現(xiàn)之后,再次運(yùn)行腳本就會(huì)得到 Podfile 中的依賴信息了态蒂,不過這里的實(shí)現(xiàn)非常簡(jiǎn)單的杭措,很多情況都沒有處理:
$ ruby eval_pod.rb
{"source"=>"http://source.git", "targets"=>["Demo"], "pods"=>["AFNetworking", "SDWebImage", "Masonry", "Typeset", "BlocksKit", "Mantle", "IQKeyboardManager", "IQDropDownTextField"]}
不過使用 Ruby 構(gòu)建一個(gè)嵌入式 DSL 的過程大概就是這樣,使用語(yǔ)言內(nèi)建的特性來進(jìn)行創(chuàng)作钾恢,創(chuàng)造出一個(gè)在使用時(shí)看起來并不像代碼的 DSL手素。
寫在后面
在最后,筆者想說的是瘩蚪,當(dāng)我們?cè)谀骋粋€(gè)領(lǐng)域經(jīng)常需要解決重復(fù)性問題時(shí)泉懦,可以考慮實(shí)現(xiàn)一個(gè) DSL 專門用來解決這些類似的問題。
而使用嵌入式 DSL 來解決這些問題是一個(gè)非常好的辦法疹瘦,我們并不需要重新實(shí)現(xiàn)解釋器崩哩,也可以利用宿主語(yǔ)言的抽象能力。
同時(shí)言沐,在嵌入式 DSL 擴(kuò)展了 DSL 的范疇之后邓嘹,不要糾結(jié)于某些東西到底是框架還是領(lǐng)域特定語(yǔ)言,這些都不重要险胰,重要的是吴超,在遇到了某些問題時(shí),我們能否跳出來鸯乃,使用文中介紹的方法減輕我們的工作量。
Reference
- [No Silver Bullet—Essence and Accidents of Software Engineering](No Silver Bullet—Essence and Accidents of Software Engineering)
- Domain-specific language
- DomainSpecificLanguage
- How browsers work
其它
GitHub Repo:iOS-Source-Code-Analyze
Follow: Draveness · GitHub
Source: http://draveness.me/dsl