J2EE之巅

 

DSL的实现要点(1)

 

引言

DSL(domain-specific language)并不是什么新的概念和技术,但是目前它已成为了一个技术热点,近期各种类型的技术交流或研讨会上你都可以看到关于DSL的主题。DSL似乎也在一夜间成为了大师们关注的焦点(Martin FowlerEric Evans等等)。

应用DSL可以有效的提高系统的可维护性(缩小了实现模型和领域模型的距离,提高了实现的可读性)和灵活性,并且提供开发的效率。

那么如何在我们的实践中引入DSL呢,Martin FowlerDSL实现模式做了全面的阐释;在实际工作中作者实践了部分Martin Fowler的模式,下文是作者对这些实践的经验总结,愿与大家分享。

根据实现方式的分类DSL可以大致分为内部DSLInternal DSL)和外部DSL(Extern DSL), 作者在实际项目中实践了这两大类DSL,在系列文章中将分别共享各类型DSL的实现经验。

示例涉及的模型

为了便于说明问题,系列文章讲围绕一个简单得示例展开,将以不同方式实现一个关于状态机描述的DSL


Figure 1状态机


Figure 2 领域模型

实现DSL的本质任务


无论是实现内部DSL或是外部DSL,要完成的本质任务就是将DSL API调用及DSL语言脚本解析为应用中的语义模型(通常为应用中领域模型的部分)。

实现DSL

实现内部DSL

内部DSL实际上就是一组精心设计的API,同时这种API及其对他的调用有着某些特定领域中自然语言的特性。

以下分享两种内部DSL实现方法的实现经验。

实现要点

不要将这种DSL API直接放置在领域模型中,这不符合关注点分离的思想,并且导致代码难以维护,应该引入一个新的层次——Builder层,由Builder层来解析调用并创建为系统中的语义模型对象。

几种模式的实现要点

方法链(Method Chain

调用示例

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);

      }

}

实现

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;

           

      }

     

}

实现要点

1 返回值

每个方法的返回都是Builder,这是为了可以实现这种链式调用。

2 继承

可以发现下一层次的Builder仍需可以提供上面层次Builder中的方法,我们把方法链拉直你便一目了然了。

             

由此可见为了避免代码重复,可以采用继承机制,让下层的Builder继承自上层的Builder

3 上下文变量(context variable

从代码中我们可以发现这些变量(各个Builder中的成员变量,process,state,tranistion,他们用来保证我们的Builder是在正确的上下文上工作,如把生成的transition加入到正确的state中。

这些上文变量在不同Builder将是通过构造函数进行传递的。

嵌套方法(Nested function)

调用示例

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")

                        )    

                       

                  })

                 

            });  

}

实现

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;

      }

}

实现要点

由源码可以看出嵌套方法的实现比方法链要简单得多。

1 方法及返回值

由于无需维护对象状态,所以方法均为静态,返回值则直接是方法所要创建的模型对象。

2 方法及方法的参数来表明语义

通过Builder中的方法来定义语法中的词汇,方法的参数表明层次与包含的语义关系。

3 无需上下文变量

由于嵌套方法实现DSL巧妙的利用了系统中的方法调用栈,所以无需采用上下文变量来进行上下文的维护。



蔡超
HP 软件架构师
软件架构顾问
SCEA
IBM Certified Solution Designer for OOA&D vUML2
Chaocai2001@yahoo.com.cn

posted on 2009-08-24 15:45 超越巅峰 阅读(2073) 评论(2)  编辑  收藏 所属分类: DSL

评论

# re: DSL的实现要点(1) 2009-08-24 17:10 99书城

不错啊!  回复  更多评论   

# re: DSL的实现要点(1) 2009-08-24 22:53 vg

camel  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(12)

随笔分类(54)

随笔档案(59)

文章分类(2)

文章档案(1)

相册

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜