摘 要:基于实际项目中的实践,介绍了DSL在软件系统中的重要价值和运行,分析DSL如何通过DSL的开发态和运行态来保证其在软件系统的运行。
关键词:DSL;业务模型;DSL开发态;DSL运行态;语法
1、DSL总体概述
DSL(Domain Specific Language),领域特定语言,Martin Fowler认为DSL自从我们使用计算机时就在使用了,但是我们一直以来缺乏对DSL进行系统性的研究和实践,截止2011年的今天,业务依然没能对DSL给出一个权威的定义。从DSL从服务的场景的来看,它是一种针对特定领域,只具备有限表达能力的语言。比如:SQL是针对关系数据库查询维护业务的特定计算机语言;CSS是用来描述HTML页面样式的特定语言;Ant用来描述任务构建和组织任务构建过程的一种特定规则语言,类似于Makefile;如表格 1所描述的特定语言用来描述某种考卷的答题结果评价:
业务描述 DSL 一种考试,满分100。0-59不及格;60-69及格;70-89良好;90以上优秀 Exam->Score:
90..100:优秀;
70..89:良好;
60.69:及格;
0..59:不及格; 表格 1 一种自定义的DSL
按照Martin Fowler的观点[1],DSL可分为内部和外部两种DSL。内部DSL,语法直接采用宿主语言的语法或者进行了适当的扩展,开发时可借鉴宿主语言的环境,直接运行在宿主语言的执行环境下,比如Rails之于Ruby。外部DSL,具有业务相关的特定语法,只能通过转换成相应的通用语言的代码并在这种通用语言的环境下执行,或者通过解析执行,比如前面介绍过的SQL、Ant等。
不同于汇编等底层语言、Java等高级通用语言,DSL只关注与软件系统所要解决的特性业务环境和业务痛点,一种DSL也只能运用在其所服务的软件系统中。因此DSL具有如下几个基本的特性:
1.1直接面向特定业务。一种DSL因软件系统所要解决的业务而生,该DSL的语法本身就具有很强的业务描述能力,而且也只是定义了业务要做成什么,而不是描述具体某个业务应该如何做。从用户的角度来看,DSL是为了提供给软件系统的用户使用的,用户说我关心的是业务是什么,然后我使用这种语言将我的业务描述出来就可以了,所以这种语言必须用业务的术语来描述。比如表格 1中的例子,抽取其语法可如所示。那么这种语法就正好是描述对于某种试卷的分数,有多少种不同的级别来评价最终的考试结果。
Exame:score=int "":""
Levels += Level
Level: from=int "".."" to=int "":"" estimate=String "";"" 表格 2 自定义DSL的语法
1.2语言简单。因为DSL就是为解决具体的业务而生,那么就只需要定义成刚刚好解决此类具体业务就可以了。而且正因为这种语言的简单易懂,对于喜欢偷懒的用户来说,他才肯接受基于此种语言来描述具体的业务。表格 1中定义的语言就非常的简单,而如果换成Java之类的语言,将需要开发更多的更复杂的代码。
1.3有限的表达能力。正因为简单,所以DSL的表达能力就有限。同时,DSL不必也不能追求能够表达更多更通用的业务场景,不然就成为Java之类的通用语言了。DSL的这个特性本身也符合软件架构设计的一个重要原则:设计刚刚好的架构,以支撑2-3的业务发展。
2、DSL在软件系统中的价值和运用
正如本文引用Martin Fowler所述的,计算机界很早就是用DSL了,并且众多的软件系统都或多或少是用不少于一种的DSL,比如当前大量的Web化的业务系统,几乎都用到了CSS、SQL等DSL。所以,DSL在软件系统中的运用时非常之广的,其价值也可见一斑。那么,DSL主要有哪些运用场景呢。
从我们的实践中,我们认为,如下的场景非常适合使用DSL:
2.1软件系统有一个清晰的业务模型,并且如果是用通用语言开发,业务模型的逻辑代码散落在系统的各个位置,难以集中维护和管理。
在业务软件的开发过程中,涉及到对业务模型的UML建模、数据库设计、业务模型的代码实现,业务数据的读取管理等,并且业务模型还需要对上层具体业务代码提供稳定的接口,那么算下来,同样的模型,在不同的地方都用到了,但是很明显这些模型散落在各种不同的物理位置中,要同步这些模型简直是非常困难的;同时,业务经常变化,那么要同步更新这些地方的模型已经不可能了。通过DSL来解决这个问题,我们可以设计出一套服务于此中业务的DSL语法,然后针对具体的业务模型编写相应的DSL代码,借助于DSL代码的生成工具,同步生成业务模型的UML模型、数据库设计、代码实现等等。但业务变化时,只需要更新该DSL语法或者代码,就可以快速的同步不同位置的代码了。
2.2软件系统中使用到了大量相似的、可重用的代码,这些代码基本通过COPY-PASTE再加上少量修改得到。
常见的例子是开发UI界面的代码。UI界面本身可以通过抽象得到UI的业务模型,这种模型在大量的通用语言都得到体现,比如Java的Swing框架和Eclpse的SWT/JFace框架。那么可以开发基于XML的用来描述UI界面的DSL语言,然后借助处理这种DSL语言的工具,自动生成相应通用语言的UI界面代码。比如YUI组件就是这么做的。那么通过这种DSL,可以为我们节省大量的工作量,也有利于充分保证整个软件系统的质量。
2.3具体业务逻辑只能由代表业务的人员才能开发指定。
首先,这种业务是开发软件的人不可能具备的,也是不可能通过编写相应的软件代码就可以支持的。比如DIY一台PC,买了一个STAT的硬盘,就必须配STAT的数据线,如果只有IDE数据线,那么要配IDE转STAT的接口;买了一个电源,这个电源需要配套几条不同类型的电源线。同时,随着IT技术发展,这些配置规则都在不断的变化。所有这些配置规则在开发软件时,都无法通过写代码完成,而是只能交给掌握业务的人员来做,软件系统支持对这些配置规则进行执行处理。那么通过定义一种直接服务于业务的DSL,使掌握业务的人员方便快速的开发。如表格2所定义的简单DSL。
2.4可以通过制定严格标准的规范加以执行,并且在大量的软件系统中可以复用。
这一类显著的例子就是SQL、CSS、HTML、ANT等等。这些DSL语言都已经是业界通用的特性语言。
2.5基于框架式编程的组件或者软件系统,整个框架在运行过程中必须对外开发业务节点,以便实现具体的业务功能,但是这些业务功能的开发必须受限于整个框架的架构和环境。
框架式编程的重要特征
就是框架负责整个业务流程的运行和生命周期管理,框架不理解业务,但是,框架是服务于一类业务的,因此必须开放出一些业务节点出来,使负责业务的人员能够开发业务功能。但是,这些业务的代码要受到整个框架的限制,那么在设计框架时,就要指定,这些业务节点只能是什么。这种场景最典型的例子就是Eclipse中的扩展点机制。开发框架时,以扩展点(Extension Point)的方式描述业务节点,具体的业务功能只能是一个个基于此扩展点的扩展(Extension)。
3、DSL在软件系统的运行
这部分主要关注外部DSL,因此本部分内容中所述的DSL都是指外部DSL。
通过前文的描述,我们知道DSL代码都是一些简单的文本,并且这些文本是现有通用语言的运行时环境不能支持的,那么开发出来的DSL代码如何帮我办法如何能够在软件系统上运行呢。通过将DSL分成开发态和运行态,可以保证。所谓DSL的开发态,指DSL在开发阶段的呈现方式和状态;DSL的运行态,指DSL真正落实到软件系统后,其执行的呈现方式和状态。继续表格1的例子,这种DSL,在开发态是表格1的呈现方式,但是这种呈现方式他无法执行在软件系统中运行,那么在运行态,它就被转换成诸如Java等通用语言的呈现方式进行运行了。如图表 2所示。
图表 2 DSL的开发态和运行态
通过图表 2我们可以看到,支撑DSL代码能够从开发到最终执行,需要经过如下几个阶段:
3.1能够在一个集成的开发环境下开发代码。
3.2通过一定的手段,将DSL代码转换成特定的通用语言,比如Java、Python等语言。
3.3在软件系统的运行环境中,需要有一个适当的框架来执行这些转换后的通用语言。比如Eclipse中的扩展点机制,用户配置了扩展之后,系统需要相应的代码来获得扩展,使用扩展中配置的信息,实例化并运行扩展中配置的Java类。
阶段1,DSL代码处于开发态,开发的主体是软件系统的用户,他基于能够理解的特定业务的语法进行开发。当然如果本身DSL代码量非常少,语法也非常简单,那么用户只需借助记事本等简单文本编辑工具就能够完成,对于规模的软件系统,这种场景是比较少的,因此提供一个集成的编辑环境(IDE)就成为一种必要。在此环境下,用户可以进行DSL代码开发、实时检查代码的语法正确性、进行所见即所得的单元测试、进行小批量的集成测试,从而提高代码开发的效率。用户开发DSL代码时,如IDE提供以文本的方式编辑代码,那么代码编辑器应当能够提供现在成熟的通用语言的编辑器该有的功能,比如undo/redo、语法着色、代码模板、重构、代码自动完成、代码Folder、实时语法检查等等。更好的方式是IDE应当支持以图形等可视化的方式编辑代码,这对用户更加直观。开发好的代码持久化时,常用的两种格式:XML格式和普通文本格式。
阶段2,DSL代码处于开发态和运行态之间。此阶段一个重要的角色就是DSL代码转换工具,保证DSL代码能够转换成特定的通用语言。通过开发代码转换模板,然后交给代码转换器,对输入的DSL代码进行转换。开发代码转换模板时,基于DSL语法结构、要生成的通用语言的代码结构两个要素进行,同时还要考虑到DFx因素,以保证生成的通用语言代码可读性,执行效率等。
以上两个阶段,都可以通过工具来支持,常用的工具有Eclipse下的Xtext、微软的DSL Tools、MetaEdit+、JetBrains MPS等。Xtext支持以文本或者图形化的方式开发DSL代码,并且代码以普通文本方式存储,借助Eclipse超强的文本编辑能力和GMF的图形表达能力,使用基于Xtext开发DSL代码效率非常高。同时,Xtext借助Xtend/Xpand支持编辑DSL代码生成通用语言代码的模板。微软的DSL Tools支持以图形化的方式开发,代码以XML格式存储,同时它借助集成TT工具来开发生成通用代码的模板。以下是引子Xtext官方网站的实例.
http://ools.