1.5 DSL的分类

DSL用领域语言来表达。领域的内涵越丰富,DSL的表现力就应当越强。对于领域用户来说,DSL帮助他理解领域的来龙去脉,至于开发者怎么实现其底层模型,这一点并不重要,只要DSL脚本能提供他对领域抽象的一致访问就行了。

最常见的分类方法是按照DSL的实现途径来分类。马丁·福勒曾将DSL分为内部外部两大类,他的分类法得到了绝大多数业界人士的认可和沿袭。内部与外部之分取决于DSL是否将一种现存语言作为宿主语言,在其上构建自身的实现。内部DSL也称内嵌式DSL,因为它们的实现嵌入到宿主语言中,与之合为一体。(内部DSL在第5章和第6章有进一步的叙述,届时我们将演示如何用Ruby、Groovy、Scala、Clojure等JVM语言实现DSL。)外部DSL也称独立DSL,因为它们是从零开始建立起来的独立语言,而不基于任何现有宿主语言的设施建立。第7章和第8章将继续介绍外部DSL。

除了这两大类,还有新出现的一些DSL开发范式。例如,Intentional Software(http://www.intentsoft.com/)等公司推出了创建非文本型DSL的工具。像这样的一些发展和增长趋势详见第9章。目前我们暂且把注意力放到两个主要类别上,通过一些例子来看下它们各自的特点。

1.5.1 内部DSL

内部DSL将一种现有编程语言作为宿主语言,基于其设施建立专门面向特定领域的各种语义。目前使用最广泛的一个内部DSL是Rails,它是在编程语言Ruby的基础上实现的。当你编写Rails代码,实际上就是在运用Rails给Web开发制定的各种语义编写Ruby程序。大多数情况下,内部DSL就是在宿主语言之上实现的库。2.1节将以Java和Groovy为宿主语言带你开发一种用于订单处理的DSL。图1-5描绘了内部DSL的结构。

enter image description here

图1-5 利用现有宿主语言及其设施来实现内部DSL

如图1-5所示,内部DSL脚本只是在用宿主语言实现的抽象上进行了少许修饰。我们再来看看外部DSL。

1.5.2 外部DSL

外部DSL是从零开发的DSL,在词法分析、解析技术、解释、编译、代码生成等方面拥有独立的设施。开发外部DSL近似于从零开始实现一种拥有独特语法和语义的全新语言。构建工具make、语法分析器生成工具YACC、词法分析工具LEX等都是常见的外部DSL。当然,外部DSL实现的复杂程度取决于你希望它有多丰富的内涵。一般来说,外部DSL并不需要像完善的语言那么复杂。第7章和第8章会给出大量示例。图1-6展示了外部DSL的结构是如何基于定制的语言设施形成的。

enter image description here

图1-6 你需要为外部DSL自行开发语言处理的设施。设施包括一般高级语言实现中常见的词法分析器、语法分析器和代码生成器等。注意,各部分的复杂程度取决于语言的精细程度

图1-6展示了外部DSL的一般组成部分。在实际开发中,上面列出的各部分未必全都用得上,而且你可能需要根据语言的复杂程度决定合并其中一些组成部分。

DSL是不是总要以文本的形式出现呢?不一定,很多时候图形化的表示更加一目了然。详情请看下文。

1.5.3 非文本DSL

除了内部和外部DSL,业界还有一种正在增长的趋势,即倾向于发展更丰富的领域建模手段。DSL是领域的一种表示形式,但其定义中并没有硬性规定这种表现形式或语言必须是文本形式的。实际上,很多人都认为程序代码用作表达领域知识的媒介太过单薄,有时无力胜任。他们常给出以下理由:

  • 文本允许的标识符号有限,限制了对领域问题的表达自由;
  • 很多领域问题可以通过电子表格、图形化模型等丰富的制品形式更好地展现给领域用户;
  • 在基于文本的脚本中,领域逻辑常散落在曲折交错的语法结构里,不经意地增加了复杂性;
  • 领域专家操作起形象化的模型总是比操作源代码更自如。

出于以上原因,一种新型的DSL正迅速成为收集和建模领域知识的新一代手段。领域用户通过一种“投影编辑器”(Projection Editor)观察和处理领域知识的表现形式。投影编辑器可以将领域的适当视图投影给用户,用户可对其做各种调整,无需编写哪怕一行代码。然后,投影编辑器在后台生成代码对用户的意图建立模型。Intentional公司的DSL Workbench(http://www.intentsoft.com)和JetBrains公司的Meta Programming System(MPS,http://www.jetbrains.com/mps)是两种可以建立丰富形态DSL的建模工具。第9章讨论基于DSL进行开发的未来趋势,列举更多这方面的例子,也更详细地介绍了这类工具提供的功能。

把DSL分成内部、外部、非文本的DSL三大类,只是广义地看待不同的DSL实现方式。若从实用性出发,你可以把非文本DSL完全看做外部DSL,因为用来实现其API的底层设施并非宿主语言。

现在我们对什么是DSL有了相当的认识,也知道如何用它们增进开发者与领域用户的沟通,那么在什么情况下需要创造一种DSL?应不应该每开发一个程序都编写一种DSL?还是说存在一些特定的条件,在那样的情况下采用基于DSL的开发特别有利?