DSL:基于規則系統組織業務規則我提出DSL,不過在該文沒有太多關于DSL的東東,顯得有些牽強。

先要說明一下什么是DSL(Domain Specific language),援引《產生式編程》一文:
“DSL(領域特定語言)是一種特化的,面向問題的語言。”
《產生式編程》對DSL的做如下分類:
1. 固定和獨立的DSL(fixed, sparate DSL),如SQL,用獨立轉換器實現的,導致技術孤島
2. 嵌入式的DSL(Embedded DSL),如類和過程,還有嵌入式的DSL
3. 模塊可組合的DSL(Modularly Composable DSL),包含兩種DSL:封裝的DSL(encapsulated DSL)和方面性(aspectual DSL)。這個兩個關系就象OOP和AOP之間的關系。
 
好了,DSL是面向問題域的。換句話說,DSL是用來解決(特定)問題
1. DSL更多的是表述特定問題域的,這是和常見編程語言的最大區別,編程語言不是面向特定的問題域的,而是一般問題域。
  1.1 DSL中關于問題域的名詞是關鍵字,而編程語言不是。
對于保險行業來說: 代理人,保單都是DSL中的關鍵字,而在編程語言中不是,需要建立起該對象(OO語言中)。
同時注意:OO追求的是一個細粒度的設計,而在DSL中可以是相對的一個粗粒度的概念。DSL中對象體系的觀念并不直接。
  1.2 DSL不處理編程語言中所涉及到的技術問題。
 
2. DSL是針對一個問題域的過程化表述。DSL基于特定問題域的關鍵字——名詞,給出一個特定問題的流程。
按我在《小議領域模型Domain Model》一文中的觀點,Domain Object都是DSL的關鍵字,而DSL表述的是Domain Service所包含的流程和規則。
 
舉個例子:
如果某學生的這個學期期末考成績90分以上超過5門,那么該學生就可以獲得一個小紅花。
 
用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();
    
// 從教務處獲取學習成績, 
    
// SchoolService表示教務處,"200602"是學期代號。
    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){
        
// 學校為該生記一個小紅花的獎章。
        SchoolService.addMedal(student, "200602"new RedFlower());
    }
}

從上面可以看出,用DSL描述很簡潔。(自己立了靶子,然后開打) 
這里我要說明的是:student和course都DSL的關鍵字,而在java代碼中不是,是開發中建立的對象體系。
DSL中沒有涉及到編程語言的技術處理問題:
1. java代碼中的轉型:(Student)studentIterator.next();
2. 臨時變量:int i = 0;
3. 新建對象: new RedFlower()
同時:DSL中我們隱藏了一個SchoolService對象,在技術上,SchoolService教務處記錄學生的成績和獎懲,student本身不帶有這些信息。
(當然關于這個例子的設計,或許有人認為student本身可以擁有這些記錄,不過由于這個course還關聯教師對象等,所以單獨管理。BTW:例子而已表太認真嘛!)
 
通常來說Use Case描述更多是是問題域的描述。
 
這里面DSL的關鍵字:所謂的問題域名詞(也就是我們所說的Domain Model)的設計和建立就是DSL的實現的關鍵,現有的手段無非就兩種:
1. 工具本身內置支持Domain Object的設計和實現。這個是正統的路子。
2. 利用外置工具實現,這個有點劍走偏鋒的意思。
 
常見的有以下幾條路子:
一.映射法。
如Drools3推出的DSL實現方式。來看看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 
 
注意這個expander orderPricing.dsl將導入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. 腳本解析:
用腳本做有天然的好處,動態類型,語法的易用性等等。
出名的有Ruby:http://www.artima.com/rubycs/articles/ruby_as_dsl.html
2. 程序解析:
ajoo同學的jaskell的dsl方案:
http://forum.javaeye.com/bloglist.php?userid=6423
http://forum.javaeye.com/viewtopic.php?t=17579&start=120
 
或許這樣分不太好,不過從本質上看,兩者都通過提供相對靈活的語法解析來使代碼簡潔更貼近問題域描述。 
  
映射法和解析法兩者都不提供Domain Model的設計支持,兩者只是在代碼生成手段上有所不同。 
在《DSL:基于規則系統組織業務規則》一文中所提出的組織業務規則的DSL就是指這兩種。 

三.元數據生成法。
這類屬于正統手法,當然也是復雜性最大的。
1. JetBrains的MPS(Meta Programming System)。
MPS的定義流程很嚴格
1. 首先定義基本的關鍵字--Struture,然后要定義其對應的editor,相當于MS VS的Designer,
2. 有了這個后就可以作為high level的模型,用來設計Model,就可以設計描述領域問題。
3. 定義low level的代碼產生器,定義轉換映射關系。
 
可以參照Drools的設計實現來理解MPS的工作原理:
1. 定義DSL文件的左表達式(MPS在這里還需要設計好editor,drools合二為一了)。
2. 基于這個表達式定義領域問題。
3. 定義DSL的右表達式,以便代碼生成。
 
MPS采用XML結構化的節點擴展能力來實現Type的定義,可以看到其GUI工具生成的mps文件是晦澀難懂的。
 
2. Microsoft的意圖編程(Intentional Programming),只在《產生式編程》中聞其大名,未見真身。
不過微軟推出了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一樣的語法結構,來定義模版(Text Template)。而不是想MPS那樣,在MPS和IntelliJ兩個工具切換來切換去的,同時也相對容易上手。
相比而言,MPS顯的龐大,VS DSL則簡單明了。
這正好反應了JAVA世界和MS世界的不同,MS一向是怎么樣簡單就怎么來,而JAVA更多像學院派風格。  
 
準備另外寫Blog詳細介紹MPS和MS的DSL Tools,這是后話。
 
另:TW的taowen同學在BJUG的google groups上有過討論,還發了一文《DSL簡單觀察報告》。有很好討論,不過不好貼出來。