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