DSL的實(shí)現(xiàn)要點(diǎn)(1)
引言
DSL(domain-specific language)并不是什么新的概念和技術(shù),但是目前它已成為了一個(gè)技術(shù)熱點(diǎn),近期各種類型的技術(shù)交流或研討會(huì)上你都可以看到關(guān)于DSL的主題。DSL似乎也在一夜間成為了大師們關(guān)注的焦點(diǎn)(Martin Fowler,Eric Evans等等)。
應(yīng)用DSL可以有效的提高系統(tǒng)的可維護(hù)性(縮小了實(shí)現(xiàn)模型和領(lǐng)域模型的距離,提高了實(shí)現(xiàn)的可讀性)和靈活性,并且提供開發(fā)的效率。
那么如何在我們的實(shí)踐中引入DSL呢,Martin Fowler就DSL實(shí)現(xiàn)模式做了全面的闡釋;在實(shí)際工作中作者實(shí)踐了部分Martin Fowler的模式,下文是作者對這些實(shí)踐的經(jīng)驗(yàn)總結(jié),愿與大家分享。
根據(jù)實(shí)現(xiàn)方式的分類DSL可以大致分為內(nèi)部DSL(Internal DSL)和外部DSL(Extern DSL), 作者在實(shí)際項(xiàng)目中實(shí)踐了這兩大類DSL,在系列文章中將分別共享各類型DSL的實(shí)現(xiàn)經(jīng)驗(yàn)。
示例涉及的模型
為了便于說明問題,系列文章講圍繞一個(gè)簡單得示例展開,將以不同方式實(shí)現(xiàn)一個(gè)關(guān)于狀態(tài)機(jī)描述的DSL。
Figure 1狀態(tài)機(jī)
Figure 2 領(lǐng)域模型
實(shí)現(xiàn)DSL的本質(zhì)任務(wù)
無論是實(shí)現(xiàn)內(nèi)部DSL或是外部DSL,要完成的本質(zhì)任務(wù)就是將DSL API調(diào)用及DSL語言腳本解析為應(yīng)用中的語義模型(通常為應(yīng)用中領(lǐng)域模型的部分)。
實(shí)現(xiàn)DSL
實(shí)現(xiàn)內(nèi)部DSL
內(nèi)部DSL實(shí)際上就是一組精心設(shè)計(jì)的API,同時(shí)這種API及其對他的調(diào)用有著某些特定領(lǐng)域中自然語言的特性。
以下分享兩種內(nèi)部DSL實(shí)現(xiàn)方法的實(shí)現(xiàn)經(jīng)驗(yàn)。
實(shí)現(xiàn)要點(diǎn)
不要將這種DSL API直接放置在領(lǐng)域模型中,這不符合關(guān)注點(diǎn)分離的思想,并且導(dǎo)致代碼難以維護(hù),應(yīng)該引入一個(gè)新的層次——Builder層,由Builder層來解析調(diào)用并創(chuàng)建為系統(tǒng)中的語義模型對象。
幾種模式的實(shí)現(xiàn)要點(diǎn)
方法鏈(Method Chain)
調(diào)用示例
publicclass Client {
publicstaticvoid main(String [] args){
Process p=new Process();
ProcessBuilder process=new ProcessBuilder(p);
process.name("Auto-Door")
.state("Open")
.transition()
.event("timeout")
.nextState("close")
.state("Close")
.transition()
.event("people-closer")
.nextState("open");
System.out.println(p);
}
}
實(shí)現(xiàn)
ProcessBuilder.java:
publicclass ProcessBuilder {
protected Process process;
public ProcessBuilder(Process process2){
this.process=process2;
}
public ProcessBuilder name(String name){
process.setName(name);
returnthis;
}
public StateBuilder state(String name){
State newState=new State();
StateBuilder sb= new StateBuilder(process,newState);
sb.name(name);
process.getStates().add(newState);
return sb;
}
}
StateBuilder.java:
public class StateBuilder extends ProcessBuilder{
protected State state=new State();
public StateBuilder(Process p,State state){
super(p);
this.state=state;
}
public StateBuilder name(String name){
state.setName(name);
returnthis;
}
public TransitionBuilder transition(){
Transition t=new Transition();
TransitionBuilder tb= new TransitionBuilder(process,state,t);
state.getTransitions().add(t);
return tb;
}
}
TransitionBuilder.java
publicclass TransitionBuilder extends StateBuilder {
private Transition transition;
public TransitionBuilder(Process process,State state,Transition transition){
super(process,state);
this.transition=transition;
}
public TransitionBuilder event(String event){
transition.setEvent(new Event(event));
returnthis;
}
public TransitionBuilder nextState(String state){
returnthis;
}
}
實(shí)現(xiàn)要點(diǎn)
1 返回值
每個(gè)方法的返回都是Builder,這是為了可以實(shí)現(xiàn)這種鏈?zhǔn)秸{(diào)用。
2 繼承
可以發(fā)現(xiàn)下一層次的Builder仍需可以提供上面層次Builder中的方法,我們把方法鏈拉直你便一目了然了。
由此可見為了避免代碼重復(fù),可以采用繼承機(jī)制,讓下層的Builder繼承自上層的Builder。
3 上下文變量(context variable)
從代碼中我們可以發(fā)現(xiàn)這些變量(各個(gè)Builder中的成員變量,process,state,tranistion),他們用來保證我們的Builder是在正確的上下文上工作,如把生成的transition加入到正確的state中。
這些上文變量在不同Builder將是通過構(gòu)造函數(shù)進(jìn)行傳遞的。
嵌套方法(Nested function)
調(diào)用示例
publicstaticvoid main(String[] args) {
Process p=process("Auto-Door", new State []{
state("Open",new Transition[]{
transition(
event("timeour"),
nextState("Close")
)
}),
state("Close",new Transition[]{
transition(
event("people-closer "),
nextState("Open")
)
})
});
}
實(shí)現(xiàn)
Builder.java
publicclass Builder {
publicstatic Process process(String name, State [] states){
Process process=new Process();
process.setName(name);
List<State> sts=new ArrayList<State>();
for (State s:states){
sts.add(s);
}
process.setStates(sts);
return process;
}
publicstatic State state(String name,Transition [] transitions){
State state=new State();
state.setName(name);
List<Transition> ts=new ArrayList<Transition>();
for (Transition t: transitions){
ts.add(t);
}
state.setTransitions(ts);
return state;
}
publicstatic Transition transition(Event event,String nextState){
Transition t=new Transition ();
t.setEvent(event);
return t;
}
publicstatic Event event(String event){
returnnew Event(event);
}
publicstatic String nextState(String nextState){
return nextState;
}
}
實(shí)現(xiàn)要點(diǎn)
由源碼可以看出嵌套方法的實(shí)現(xiàn)比方法鏈要簡單得多。
1 方法及返回值
由于無需維護(hù)對象狀態(tài),所以方法均為靜態(tài),返回值則直接是方法所要?jiǎng)?chuàng)建的模型對象。
2 方法及方法的參數(shù)來表明語義
通過Builder中的方法來定義語法中的詞匯,方法的參數(shù)表明層次與包含的語義關(guān)系。
3 無需上下文變量
由于嵌套方法實(shí)現(xiàn)DSL巧妙的利用了系統(tǒng)中的方法調(diào)用棧,所以無需采用上下文變量來進(jìn)行上下文的維護(hù)。
蔡超
HP 軟件架構(gòu)師
軟件架構(gòu)顧問
SCEA
IBM Certified Solution Designer for OOA&D vUML2
Chaocai2001@yahoo.com.cn
posted on 2009-08-24 15:45 超越巔峰 閱讀(2088) 評論(2) 編輯 收藏 所屬分類: DSL