在
DSL:基于規(guī)則系統(tǒng)組織業(yè)務(wù)規(guī)則我提出DSL,不過在該文沒有太多關(guān)于DSL的東東,顯得有些牽強(qiáng)。
先要說明一下什么是DSL(Domain Specific language),援引《產(chǎn)生式編程》一文:
“DSL(領(lǐng)域特定語言)是一種
特化的,面向問題的語言。”
《產(chǎn)生式編程》對(duì)DSL的做如下分類:
1. 固定和獨(dú)立的DSL(fixed, sparate DSL),如SQL,用獨(dú)立轉(zhuǎn)換器實(shí)現(xiàn)的,導(dǎo)致技術(shù)孤島
2. 嵌入式的DSL(Embedded DSL),如類和過程,還有嵌入式的DSL
3. 模塊可組合的DSL(Modularly Composable DSL),包含兩種DSL:封裝的DSL(encapsulated DSL)和方面性(aspectual DSL)。這個(gè)兩個(gè)關(guān)系就象OOP和AOP之間的關(guān)系。
好了,DSL是
面向問題域的。換句話說,DSL是用來
解決(特定)問題。
1. DSL更多的是表述特定問題域的,這是和常見編程語言的最大區(qū)別,
編程語言不是面向特定的問題域的,而是一般問題域。
1.1 DSL中關(guān)于問題域的名詞是關(guān)鍵字,而編程語言不是。
對(duì)于保險(xiǎn)行業(yè)來說: 代理人,保單都是DSL中的關(guān)鍵字,而在編程語言中不是,需要建立起該對(duì)象(OO語言中)。
同時(shí)注意:OO追求的是一個(gè)細(xì)粒度的設(shè)計(jì),而在DSL中可以是相對(duì)的一個(gè)粗粒度的概念。DSL中對(duì)象體系的觀念并不直接。
1.2 DSL不處理編程語言中所涉及到的技術(shù)問題。
2. DSL是針對(duì)一個(gè)問題域的過程化表述。DSL基于特定問題域的關(guān)鍵字——名詞,給出一個(gè)特定問題的流程。
按我在《小議領(lǐng)域模型Domain Model》一文中的觀點(diǎn),Domain Object都是DSL的關(guān)鍵字,而DSL表述的是Domain Service所包含的流程和規(guī)則。
舉個(gè)例子:
如果某學(xué)生的這個(gè)學(xué)期期末考成績(jī)90分以上超過5門,那么該學(xué)生就可以獲得一個(gè)小紅花。
用DSL來描述就可能是:
foreach students
if the total number of { the courses of {the student} {this term} that {it's grade > 90} } > 5 then
the student get a red flower
end if
end foreach
而用Java程序代碼(假想代碼)
Iterator studentIterator = students.iterator();
while(studentIterator.hasNext()){
Student student = (Student)studentIterator.next();
// 從教務(wù)處獲取學(xué)習(xí)成績(jī),
// SchoolService表示教務(wù)處,"200602"是學(xué)期代號(hào)。
CourseGrade[] grades = SchoolService.getCourseGrade(student, "200602");
int i = 0;
Iterator gradeIterator = grades.iterator();
while(gradeIterator.hasNext()){
CourseGrade courseGrade = (CourseGrade)gradeIterator.next();
if(courseGrade.getGrade() > 90){
i++;
}
}
if(i>5){
// 學(xué)校為該生記一個(gè)小紅花的獎(jiǎng)?wù)隆?/span>
SchoolService.addMedal(student, "200602", new RedFlower());
}
}
從上面可以看出,用
DSL描述很簡(jiǎn)潔。(自己立了靶子,然后開打)
這里我要說明的是:student和course都DSL的關(guān)鍵字,而在java代碼中不是,是開發(fā)中建立的對(duì)象體系。
DSL中沒有涉及到編程語言的技術(shù)處理問題:
1. java代碼中的轉(zhuǎn)型:(Student)studentIterator.next();
2. 臨時(shí)變量:int i = 0;
3. 新建對(duì)象: new RedFlower()
同時(shí):DSL中我們隱藏了一個(gè)SchoolService對(duì)象,在技術(shù)上,SchoolService教務(wù)處記錄學(xué)生的成績(jī)和獎(jiǎng)懲,student本身不帶有這些信息。
(當(dāng)然關(guān)于這個(gè)例子的設(shè)計(jì),或許有人認(rèn)為student本身可以擁有這些記錄,不過由于這個(gè)course還關(guān)聯(lián)教師對(duì)象等,所以單獨(dú)管理。BTW:例子而已表太認(rèn)真嘛!)
通常來說Use Case描述更多是是問題域的描述。
這里面DSL的關(guān)鍵字:所謂的問題域名詞(也就是我們所說的Domain Model)的設(shè)計(jì)和建立就是DSL的實(shí)現(xiàn)的關(guān)鍵,現(xiàn)有的手段無非就兩種:
1. 工具本身
內(nèi)置支持Domain Object的設(shè)計(jì)和實(shí)現(xiàn)。這個(gè)是正統(tǒng)的路子。
2. 利用
外置工具實(shí)現(xiàn),這個(gè)有點(diǎn)劍走偏鋒的意思。
常見的有以下幾條路子:
一.映射法。
如Drools3推出的DSL實(shí)現(xiàn)方式。來看看
springside的例子
package org.springside.dsl
import org.springside.bookstore.domain.Order
import java.lang.Double
import java.math.BigDecimal
expander orderPricing.dsl
rule " order 0.9 discount"
when
order price larger than 100
then
do 0.9 discount
#加入 > 就可以在drl/dsl 中直接寫入drl 語法
>System.out.println("original price:"+order.getOriginalPrice()+" discount price:"+order.getTotalPrice());
end
注意這個(gè)expander orderPricing.dsl將導(dǎo)入DSL映射:
[when]order price larger than {topPrice}=order : Order( totalPrice >= {topPrice} )
[then]do {discountRate} discount=order.setTotalPrice(new Double(new BigDecimal(order.getTotalPrice().doubleValue() * {discountRate}).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue()));
Oracle的Rule編輯器和JRule也屬于這一類。
二.解析法
1. 腳本解析:
用腳本做有天然的好處,動(dòng)態(tài)類型,語法的易用性等等。
出名的有Ruby:http://www.artima.com/rubycs/articles/ruby_as_dsl.html
2. 程序解析:
ajoo同學(xué)的jaskell的dsl方案:
http://forum.javaeye.com/bloglist.php?userid=6423
http://forum.javaeye.com/viewtopic.php?t=17579&start=120
或許這樣分不太好,不過從本質(zhì)上看,兩者都通過提供相對(duì)靈活的語法解析來使代碼簡(jiǎn)潔更貼近問題域描述。
映射法和解析法兩者都不提供Domain Model的設(shè)計(jì)支持,兩者只是在代碼生成手段上有所不同。
在《DSL:基于規(guī)則系統(tǒng)組織業(yè)務(wù)規(guī)則》一文中所提出的組織業(yè)務(wù)規(guī)則的DSL就是指這兩種。 三.元數(shù)據(jù)生成法。
這類屬于正統(tǒng)手法,當(dāng)然也是復(fù)雜性最大的。
1. JetBrains的MPS(Meta Programming System)。
MPS的定義流程很嚴(yán)格
1. 首先定義基本的關(guān)鍵字--Struture,然后要定義其對(duì)應(yīng)的editor,相當(dāng)于MS VS的Designer,
2. 有了這個(gè)后就可以作為high level的模型,用來設(shè)計(jì)Model,就可以設(shè)計(jì)描述領(lǐng)域問題。
3. 定義low level的代碼產(chǎn)生器,定義轉(zhuǎn)換映射關(guān)系。
可以參照Drools的設(shè)計(jì)實(shí)現(xiàn)來理解MPS的工作原理:
1. 定義DSL文件的左表達(dá)式(MPS在這里還需要設(shè)計(jì)好editor,drools合二為一了)。
2. 基于這個(gè)表達(dá)式定義領(lǐng)域問題。
3. 定義DSL的右表達(dá)式,以便代碼生成。
MPS采用XML結(jié)構(gòu)化的節(jié)點(diǎn)擴(kuò)展能力來實(shí)現(xiàn)Type的定義,可以看到其GUI工具生成的mps文件是晦澀難懂的。
2. Microsoft的意圖編程(Intentional Programming),只在《產(chǎn)生式編程》中聞其大名,未見真身。
不過微軟推出了VS.Net的DSL Tools,不知道是不是意圖編程的成果。
http://msdn.microsoft.com/vstudio/DSLTools/http://www.microsoft.com/downloads/details.aspx?familyid=57a14cc6-c084-48dd-b401-1845013bf834&displaylang=en MS DSL的model定義也是采用XML,沒有designer的幫助,也是晦澀難懂的。
不過,其模版生成采用類似ASP.NET一樣的語法結(jié)構(gòu),來定義模版(Text Template)。而不是想MPS那樣,在MPS和IntelliJ兩個(gè)工具切換來切換去的,同時(shí)也相對(duì)容易上手。
相比而言,MPS顯的龐大,VS DSL則簡(jiǎn)單明了。
這正好反應(yīng)了JAVA世界和MS世界的不同,MS一向是怎么樣簡(jiǎn)單就怎么來,而JAVA更多像學(xué)院派風(fēng)格。
準(zhǔn)備另外寫B(tài)log詳細(xì)介紹MPS和MS的DSL Tools,這是后話。
另:TW的taowen同學(xué)在BJUG的google groups上有過討論,還發(fā)了一文《DSL簡(jiǎn)單觀察報(bào)告》。有很好討論,不過不好貼出來。