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