為何需要DSL
DSL只是一種工具,關(guān)注點(diǎn)有限,無法像面向?qū)ο缶幊袒蛎艚莘椒ㄕ撃菢?,引發(fā)軟件開發(fā)思考方式的深刻變革。相反,它是在特定條件下有專門用途的一種工具。一個(gè)普通的項(xiàng)目可能在多個(gè)地方采用了多種DSL——事實(shí)上很多項(xiàng)目這么做了。
DSL有其自身的價(jià)值。當(dāng)考慮采用DSL時(shí),要仔細(xì)衡量它的哪些價(jià)值適合于我們的情況。
1)提高開發(fā)效率
DSL的核心價(jià)值在于,它提供了一種手段,可以更加清晰地就系統(tǒng)某部分的意圖進(jìn)行溝通。拿格蘭特小姐控制器的定義來說,相比于采用命令–查詢API,DSL形式對我們而言更容易理解。
這種清晰并非只是審美追求。一段代碼越容易看懂,就越容易發(fā)現(xiàn)錯(cuò)誤,也就越容易對系統(tǒng)進(jìn)行修改。因此,我們鼓勵(lì)變量名要有意義,文檔要寫清楚,代碼結(jié)構(gòu)要寫清晰?;谕瑯拥睦碛桑覀儜?yīng)該也鼓勵(lì)采用DSL。
人們經(jīng)常低估缺陷對生產(chǎn)率的影響。缺陷不僅損害軟件的外部質(zhì)量,還浪費(fèi)開發(fā)人員的時(shí)間去調(diào)查以及修復(fù),降低開發(fā)效率,并使系統(tǒng)的行為異常,播下混亂的種子。DSL的受限表達(dá)性,使其難于犯錯(cuò),縱然犯錯(cuò),也易于發(fā)現(xiàn)。
模型本身可以極大地提升生產(chǎn)率。通過把公共代碼放在一起,它可以避免重復(fù)。首先,它提供了一種“用于思考問題”的抽象,這樣,更容易用一種可理解的方式指定系統(tǒng)行為。DSL提供了一種“對閱讀和操作抽象”更具表達(dá)性的形式,從而增強(qiáng)了這種抽象。DSL還可以幫助人們更好地學(xué)習(xí)使用API,因?yàn)樗鼘⑷藗兊年P(guān)注點(diǎn)轉(zhuǎn)移到怎樣將API方法整合在一起。
我還遇到過一個(gè)有趣的例子,使用DSL封裝一個(gè)棘手的第三方程序庫。當(dāng)命令–查詢接口設(shè)計(jì)得很糟糕時(shí),DSL 慣常的連貫性就得以凸現(xiàn)。此外,DSL只須支持客戶真正用到的部分,這大大降低了客戶開發(fā)人員學(xué)習(xí)的成本。
2)與領(lǐng)域?qū)<业臏贤?/strong>
我相信,軟件項(xiàng)目中最困難的部分,也是項(xiàng)目失敗最常見的原因,就是開發(fā)團(tuán)隊(duì)與客戶以及軟件用戶之間的溝通。DSL提供了一種清晰而準(zhǔn)確的語言,可以有效地改善這種溝通。
相比于關(guān)于生產(chǎn)率的簡單爭論,改善溝通所帶來的好處顯得更加微妙。首先,很多DSL并不適用于溝通領(lǐng)域問題,比如,用于正則表達(dá)式或構(gòu)建依賴關(guān)系的DSL,在這些情況下就不合適。只有一部分獨(dú)立DSL確實(shí)應(yīng)用這種溝通手段。
當(dāng)在這樣的場景下討論DSL時(shí),經(jīng)常會有人說:“好吧,現(xiàn)在我們不需要程序員了,領(lǐng)域?qū)<铱梢宰约褐付I(yè)務(wù)規(guī)則?!蔽野堰@種論調(diào)稱為“COBOL謬論”——因?yàn)镃OBOL曾被人寄予這樣的厚望。這種爭論很常見,不過,我覺得這種爭論不值得在此重復(fù)。
雖然存在“COBOL謬論”,我仍然覺得DSL可以改善溝通。不是讓領(lǐng)域?qū)<易约喝慏SL,但他們可以讀懂,從而理解系統(tǒng)做了什么。能夠閱讀DSL代碼,領(lǐng)域?qū)<揖涂梢灾赋鰡栴}所在。他們還可以同編寫業(yè)務(wù)規(guī)則的程序員更好地交流,也許,他們還可以編寫一些草稿,程序員們可以將其細(xì)化成適當(dāng)?shù)腄SL規(guī)則。
但我不是說領(lǐng)域?qū)<矣肋h(yuǎn)不能編寫DSL。我遇見過很多團(tuán)隊(duì),他們成功地讓領(lǐng)域?qū)<矣肈SL編寫了大量系統(tǒng)功能。但我仍然認(rèn)為,使用DSL的最大價(jià)值在于,領(lǐng)域?qū)<夷軌蜃x懂。所以編寫DSL的第一步,應(yīng)該專注于易讀性,這樣即便后續(xù)的目標(biāo)達(dá)不到,我們也不會失去什么。
使用DSL是為了讓領(lǐng)域?qū)<夷軌蚩炊?,這就引出了一個(gè)值得爭議的問題。如果希望領(lǐng)域?qū)<依斫庖粋€(gè)“語義模型”的內(nèi)容,可以將模型可視化。這時(shí)就要考慮一下,相比于支持一種DSL,是不是只使用可視化會是一種更有效的辦法??梢暬瘜τ贒SL而言,是一種有益的補(bǔ)充。
讓領(lǐng)域?qū)<覅⑴c構(gòu)建DSL,與讓他們參與構(gòu)建模型是同樣的道理。我發(fā)現(xiàn),與領(lǐng)域?qū)<乙黄饦?gòu)建模型能夠帶來很大的好處,在構(gòu)建一種Ubiquitous Language [Evans DDD] 的過程中,程序員與領(lǐng)域?qū)<抑g可以深入溝通。DSL提供了另一種增進(jìn)溝通的手段。隨著項(xiàng)目的不同,我們可能發(fā)現(xiàn),領(lǐng)域?qū)<铱赡軙⑴c模型和DSL,也可能只參與 DSL。
實(shí)際上,有些人發(fā)現(xiàn),即便不實(shí)現(xiàn)DSL,有一種描述領(lǐng)域知識的DSL,也能帶來很大的好處。即使只把它當(dāng)做溝通平臺也可以獲益。
總的來說,讓領(lǐng)域?qū)<覅⑴c構(gòu)建DSL比較難,但回報(bào)很高。即使最終不能讓領(lǐng)域?qū)<覅⑴c,但是開發(fā)人員在生產(chǎn)率方面的提升,也足以讓我們大受裨益,因此,DSL值得投入。
3)執(zhí)行環(huán)境的改變
當(dāng)談及將狀態(tài)機(jī)表述為XML的理由時(shí),一個(gè)重要的原因是,狀態(tài)機(jī)定義可以在運(yùn)行時(shí)解析,而非編譯時(shí)。在這種情況下,我們希望將代碼運(yùn)行于不同的環(huán)境,這類理由也是使用DSL一個(gè)常見的驅(qū)動力。對于XML配置文件而言,將邏輯從編譯時(shí)移到運(yùn)行時(shí)就是一個(gè)這樣的理由。
還有一些需要遷移執(zhí)行環(huán)境的情況。我曾見過一個(gè)項(xiàng)目,它要從數(shù)據(jù)庫里找出所有滿足某種條件的合同,給它們打上標(biāo)簽。他們編寫了一種DSL,以指定這些條件,并用它以Ruby語言組裝“語義模型”。如果用Ruby將所有合同讀入內(nèi)存,再運(yùn)行查詢邏輯,那會非常慢,但是團(tuán)隊(duì)可以用語義模型的表示生成SQL,在數(shù)據(jù)庫里做處理。直接用SQL編寫規(guī)則,對開發(fā)人員都很困難,遑論業(yè)務(wù)人員。然而,業(yè)務(wù)人員可以讀懂(在這種情況下,甚至編寫)DSL里有關(guān)的表達(dá)式。
這樣用DSL常??梢詮浹a(bǔ)宿主語言的局限性,將事物以適宜的DSL形式表現(xiàn)出來,然后,生成可用于實(shí)際執(zhí)行環(huán)境的代碼。
模型的存在有助于這種遷移。一旦有了一個(gè)模型,或者直接執(zhí)行它,或者根據(jù)它產(chǎn)生代碼都很容易。模型可以由表單風(fēng)格的界面創(chuàng)建,也可以由DSL創(chuàng)建。DSL相對于表單有一些優(yōu)勢。在表述復(fù)雜邏輯方面,DSL比表單做得更好。而且,可以用相同的代碼管理工具,比如版本控制系統(tǒng),管理這些規(guī)則。當(dāng)規(guī)則經(jīng)由表單輸入,存入數(shù)據(jù)庫中,版本控制就無能為力了。
下面會談及DSL的一個(gè)偽優(yōu)點(diǎn)。我聽說,有人宣稱DSL的一個(gè)好處是,它能夠在不同的語言環(huán)境下執(zhí)行相同的行為。一個(gè)人編寫了業(yè)務(wù)規(guī)則,然后生成C#或Java代碼,或者,描述校驗(yàn)邏輯之后,在服務(wù)器端以C#形式運(yùn)行,在客戶端則是JavaScript。這是一個(gè)偽優(yōu)勢,因?yàn)閮H僅使用模型就可以做到這一點(diǎn),根本無需DSL。當(dāng)然,DSL有助于理解這些規(guī)則,但那是另外一個(gè)問題。
4)其他計(jì)算模型
幾乎所有主流的編程語言都采用命令式的計(jì)算模型。這意味著,我們要告訴計(jì)算機(jī)做什么事情,按照怎樣的順序來做。通過條件和循環(huán)處理控制流,還要使用變量——確實(shí),還有很多我們以為理所當(dāng)然的東西。命令式計(jì)算模型之所以流行,是因?yàn)樗鼈兿鄬θ菀桌斫?,也容易?yīng)用到許多問題上。然而,它并不總是最佳選擇。
狀態(tài)機(jī)是這方面的一個(gè)良好例子??梢圆捎妹钍酱a和條件處理這種行為,也確實(shí)可以很好地構(gòu)建出這種行為。但如果直接把它當(dāng)做“狀態(tài)機(jī)”來思考,效果會更好。另外一個(gè)常見的例子是,定義軟件構(gòu)建方式。我們固然可以用命令式邏輯實(shí)現(xiàn)它,但后來,人們發(fā)現(xiàn)用“依賴網(wǎng)絡(luò)”(比如,運(yùn)行測試必須依賴于最新的編譯結(jié)果)解決會更容易。結(jié)果,人們設(shè)計(jì)出了專用于描述構(gòu)建的語言(比如Make和Ant),其中將任務(wù)間的依賴關(guān)系作為主要的結(jié)構(gòu)化機(jī)制。
你可能經(jīng)常聽到,人們把非命令式方式稱為聲明式編程。之所以叫做聲明式,是因?yàn)檫@種風(fēng)格讓人定義做什么,而不是用一堆命令語句來描述怎么做。
采用其他計(jì)算模型,并不一定非要有DSL。其他編程模型的核心行為也源自“語義模型”,正如前面所講的狀態(tài)機(jī)。然而,DSL還是能夠帶來很大的轉(zhuǎn)變,因?yàn)椴僮髀暶魇匠绦?,組裝語義模型會容易一些。
評論