開(kāi)發(fā)者需要了解的領(lǐng)域特定語(yǔ)言(DSL)
領(lǐng)域特定語(yǔ)言是在特定領(lǐng)域下用于特定上下文的語(yǔ)言。作為開(kāi)發(fā)者,很有必要了解領(lǐng)域特定語(yǔ)言的含義,以及為什么要使用特定領(lǐng)域語(yǔ)言。
領(lǐng)域特定語(yǔ)言(DSL)是一種旨在特定領(lǐng)域下的上下文的語(yǔ)言。這里的領(lǐng)域是指某種商業(yè)上的(例如銀行業(yè)、保險(xiǎn)業(yè)等)上下文,也可以指某種應(yīng)用程序的(例如 Web 應(yīng)用、數(shù)據(jù)庫(kù)等)上下文。與之相比的另一個(gè)概念是通用語(yǔ)言(GPL,LCTT 譯注:注意不要和 GPL 許可證混淆),通用語(yǔ)言則可以廣泛應(yīng)用于各種商業(yè)或應(yīng)用問(wèn)題當(dāng)中。
DSL 并不具備很強(qiáng)的普適性,它是僅為某個(gè)適用的領(lǐng)域而設(shè)計(jì)的,但它也足以用于表示這個(gè)領(lǐng)域中的問(wèn)題以及構(gòu)建對(duì)應(yīng)的解決方案。HTML 是 DSL 的一個(gè)典型,它是在 Web 應(yīng)用上使用的語(yǔ)言,盡管 HTML 無(wú)法進(jìn)行數(shù)字運(yùn)算,但也不影響它在這方面的廣泛應(yīng)用。
而 GPL 則沒(méi)有特定針對(duì)的領(lǐng)域,這種語(yǔ)言的設(shè)計(jì)者不可能知道這種語(yǔ)言會(huì)在什么領(lǐng)域被使用,更不清楚用戶(hù)打算解決的問(wèn)題是什么,因此 GPL 會(huì)被設(shè)計(jì)成可用于解決任何一種問(wèn)題、適合任何一種業(yè)務(wù)、滿足任何一種需求。例如 Java 就屬于 GPL,它可以在 PC 或移動(dòng)設(shè)備上運(yùn)行,嵌入到銀行、金融、保險(xiǎn)、制造業(yè)等各種行業(yè)的應(yīng)用中去。
DSL 的類(lèi)別
從使用方式的角度,語(yǔ)言可以劃分出以下兩類(lèi):
- DSL:使用 DSL 形式編寫(xiě)或表示的語(yǔ)言
- 宿主語(yǔ)言:用于執(zhí)行或處理 DSL 的語(yǔ)言
由不同的語(yǔ)言編寫(xiě)并由另一種宿主語(yǔ)言處理的 DSL 被稱(chēng)為外部 DSL。
以下就是可以在宿主語(yǔ)言中處理的 SQL 形式的 DSL:
SELECT account
FROM accounts
WHERE account = '123' AND branch = 'abc' AND amount >= 1000
因此,只要在規(guī)定了詞匯和語(yǔ)法的情況下,DSL 也可以直接使用英語(yǔ)來(lái)編寫(xiě),并使用諸如 ANTLR 這樣的解析器生成器以另一種宿主語(yǔ)言來(lái)處理 DSL:
if smokes then increase premium by 10%
如果 DSL 和宿主語(yǔ)言是同一種語(yǔ)言,這種 DSL 稱(chēng)為內(nèi)部DSL,其中 DSL 由以同一種語(yǔ)義的宿主語(yǔ)言編寫(xiě)和處理,因此又稱(chēng)為嵌入式 DSL。以下是兩個(gè)例子:
-
Bash 形式的 DSL 可以由 Bash 解釋器執(zhí)行:
if today_is_christmas; then apply_christmas_discount; fi
同時(shí)這也是一段看起來(lái)符合英語(yǔ)語(yǔ)法的 Bash。
-
使用類(lèi)似 Java 語(yǔ)法編寫(xiě)的 DSL:
orderValue = orderValue
.applyFestivalDiscount()
.applyCustomerLoyalityDiscount()
.applyCustomerAgeDiscount();
這一段的可讀性也相當(dāng)強(qiáng)。
實(shí)際上,DSL 和 GPL 之間并沒(méi)有非常明確的界限。
DSL 家族
以下這些語(yǔ)言都可以作為 DSL 使用:
- Web 應(yīng)用:HTML
- Shell:用于類(lèi) Unix 系統(tǒng)的 sh、Bash、CSH 等;用于 Windows 系統(tǒng)的 MS-DOS、Windows Terminal、PowerShell 等
- 標(biāo)記語(yǔ)言:XML
- 建模:UML
- 數(shù)據(jù)處理:SQL 及其變體
- 業(yè)務(wù)規(guī)則管理:Drools
- 硬件:Verilog、VHD
- 構(gòu)建工具:Maven、Gradle
- 數(shù)值計(jì)算和模擬:MATLAB(商業(yè))、GNU Octave、Scilab
- 解析器和生成器:Lex、YACC、GNU Bison、ANTLR
為什么要使用 DSL?
DSL 的目的是在某個(gè)領(lǐng)域中記錄一些需求和行為,在某些方面(例如金融商品交易)中,DSL 的適用場(chǎng)景可能更加狹窄。業(yè)務(wù)團(tuán)隊(duì)和技術(shù)團(tuán)隊(duì)能通過(guò) DSL 有效地協(xié)同工作,因此 DSL 除了在業(yè)務(wù)用途上有所發(fā)揮,還可以讓設(shè)計(jì)人員和開(kāi)發(fā)人員用于設(shè)計(jì)和開(kāi)發(fā)應(yīng)用程序。
DSL 還可以用于生成一些用于解決特定問(wèn)題的代碼,但生成代碼并不是 DSL 的重點(diǎn)并不在此,而是對(duì)專(zhuān)業(yè)領(lǐng)域知識(shí)的結(jié)合。當(dāng)然,代碼生成在領(lǐng)域工程中是一個(gè)巨大的優(yōu)勢(shì)。
DSL 的優(yōu)點(diǎn)和缺點(diǎn)
DSL 的優(yōu)點(diǎn)是,它對(duì)于領(lǐng)域的特征捕捉得非常好,同時(shí)它不像 GPL 那樣包羅萬(wàn)有,學(xué)習(xí)和使用起來(lái)相對(duì)比較簡(jiǎn)單。因此,它在專(zhuān)業(yè)人員之間、專(zhuān)業(yè)人員和開(kāi)發(fā)人員之間都提供了一個(gè)溝通的橋梁。
而 DSL 最顯著的缺點(diǎn)就在于它只能用于一個(gè)特定的領(lǐng)域和目標(biāo)。盡管學(xué)習(xí)起來(lái)不算太難,但學(xué)習(xí)成本仍然存在。如果使用到 DSL 相關(guān)的工具,即使對(duì)工作效率有所提升,但開(kāi)發(fā)或配置這些工具也會(huì)增加一定的工作負(fù)擔(dān)。另外,如果要設(shè)計(jì)一款 DSL,設(shè)計(jì)者必須具備專(zhuān)業(yè)領(lǐng)域知識(shí)和語(yǔ)言開(kāi)發(fā)知識(shí),而同時(shí)具備這兩種知識(shí)的人卻少之又少。
DSL 相關(guān)軟件
開(kāi)源的 DSL 軟件包括:
- Xtext:Xtext 可以與 Eclipse 集成,并支持 DSL 開(kāi)發(fā)。它能夠?qū)崿F(xiàn)代碼生成,因此一些開(kāi)源和商業(yè)產(chǎn)品都用它來(lái)提供特定的功能。用于農(nóng)業(yè)活動(dòng)建模分析的多用途農(nóng)業(yè)數(shù)據(jù)系統(tǒng)(MADS)就是基于 Xtext 實(shí)現(xiàn)的一個(gè)項(xiàng)目,可惜的是這個(gè)項(xiàng)目現(xiàn)在已經(jīng)不太活躍了。
- JetBrains MPS:JetBrains MPS 是一個(gè)可供開(kāi)發(fā) DSL 的集成開(kāi)發(fā)環(huán)境,它將文檔在底層存儲(chǔ)為一個(gè)抽象樹(shù)結(jié)構(gòu)(Microsoft Word 也使用了這一概念),因此它也自稱(chēng)為一個(gè)投影編輯器。JetBrains MPS 支持 Java、C、JavaScript 和 XML 的代碼生成。
DSL 的優(yōu)秀實(shí)踐
如果你想使用 DSL,記住以下幾點(diǎn):
- DSL 不同于 GPL,DSL 只能用于解決特定領(lǐng)域中有限范圍內(nèi)的問(wèn)題。
- 不必動(dòng)輒建立自己的 DSL,可以首先嘗試尋找已有的 DSL。例如 DSLFIN 這個(gè)網(wǎng)站就提供了很多金融方面的 DSL。在實(shí)在找不到合適的 DSL 的情況下,才需要建立自己的 DSL。
- DSL 最好像平常的語(yǔ)言一樣具有可讀性。
- 盡管代碼生成不是一項(xiàng)必需的工作,但它確實(shí)會(huì)大大提高工作效率。
- 雖然 DSL 被稱(chēng)為語(yǔ)言,但 DSL 不需要像 GPL 一樣可以被執(zhí)行,可執(zhí)行性并不是 DSL 需要達(dá)到的目的。
- DSL 可以使用文本編輯器編寫(xiě),但專(zhuān)門(mén)的 DSL 編輯器可以更輕松地完成 DSL 的語(yǔ)法和語(yǔ)義檢查。