前言本专题主要介绍在Visual Studio 2012中使用Visualization & Modeling SDK进行领域特定语言(DSL)的开发,包括两个部分的内容。在第一部分中,将对领域特定语言进行简单介绍,并讲解如何使用Visual Studio 2012创建一个领域特定语言的开发解决方案,以及Visual Studio 2012集成开发环境对DSL开发的支持;在第二部分中,将以实际应用为例,介绍开发DSL的主要步骤,包括设计、定制、调试、发布以及使用等。本文为本专题的第一部分。 领域特定语言概述在软件开发过程中,领域特定语言(Domain Specific Language,DSL)可以是一种面向特定应用领域的编程语言,可以是针对某一类问题的表述形式或解决方案,也可以是一种规约(Specification)集合。给DSL下一个明确的定义并不是件容易的事情,比如XML,在大多数情况下,它当然不是DSL,因为能够使用XML进行描述的信息种类数不胜数,但如果我们把XML用于.NET应用程序的配置中,让配置文件以XML的形式进行组织,那么这种具有特定结构的XML就可以看成是一种DSL:这种DSL面向.NET应用程序的配置领域,为这一领域中的问题提供解决方案。在这里,我们暂且不讨论用XML来实现某种DSL是否合适。 对领域驱动设计(Domain Driven Design,DDD)有一定了解的读者,一定对“通用语言(Ubiquitous Language)”这一词语并不陌生。不错,通用语言是DDD的讨论核心,换句话说,所有与DDD相关的理念和实践,都是围绕通用语言而存在和进行的。通用语言解决了软件系统(或者说应用程序)开发过程中的一个重要问题:沟通。目前的现状是:领域专家(业务人员)对软件开发技术和过程并不了解,甚至是一无所知;而软件开发人员又对其所涉及的领域难以理解。所以,DDD认为,应该基于软件系统所面临的领域,设计一套能够同时被领域专家和软件开发人员所理解的语言,以便尽量减少由沟通引起的误解和分歧。这就是通用语言的由来,这里我们还需要区分一下“通用语言”和“普通编程语言(General-Purpose Programming Language)”的差别,虽然两者都有“通用”的含义,但前者是指“在领域专家和软件开发人员之间的通用性”。 由此引出两个问题:首先,通用语言的定义和设计是需要一定成本的,这对于经费本不充裕的项 “通用语言”就是面向特定领域的DSL,根据现行的DSL分类方式,“通用语言”基本上都是以“外部DSL”的方式实现,因为嵌入式(或者说“内部”)DSL都需要一种“普通编程语言”作为宿主,但领域专家不一定熟悉普通编程语言。相比之下,UML就不是一种DSL,因为UML并非专注于一个特定的领域。 DSL一般分为两种:嵌入式(内部)DSL(Embedded/Internal DSL)和外部DSL(External DSL)。上面也简单提到过,内部DSL通常需要一种普通编程语言作为宿主,就编译器角度而言,内部DSL和普通编程语言之间没有差别,但对于开发人员来说,内部DSL提供了一种可读性更强的、更能表达特定领域语义的编程方式。较为流行的内部DSL实现方式就是我们熟知的“流畅接口”(Fluent Interface):通过一系列属性和方法的连贯调用,让人觉得好像是在使用一种自然语言来表达和解决特定领域的具体问题。在.NET中,LINQ就是这样一种内部DSL,它以另一种形式集成于宿主编程语言(C#或VB.NET)中,开发人员可以通过这种形式的代码来很顺利地表达查询语义;从编译器的角度看,LINQ形式的代码其实就是流畅接口的调用,如果您对这个问题感兴趣的话,您可以使用C#语言写两个方法,在A方法中使用LINQ方式对一个列表结构进行筛选,比如:var query = from l in list where l == 3 select l;;而在B方法中则使用System.Linq命名空间中的扩展方法,以流畅接口的方式对列表结构进行同样的筛选操作,比如:var query = list.Where(l => l == 3);,当你使用ildasm.exe工具查看这两个方法所产生的IL代码时,你会发现两者完全相同。至于“流畅接口”的实现方式,我已经在《在C#中使用装饰器模式和扩展方法实现Fluent Interface》一文中给出了解决方案。 另一种是外部DSL,外部DSL的实现通常需要借助于新的编译器/解释器,这也就意味着团队需要设计并开发一套能够将外部DSL翻译成普通编程语言,或者能够将外部DSL转换为能在程序环境中运行的组件的编译器/解释器。外部DSL也有两种不同的形式:文本式的和图形化的。文本式的优点在于维护简单,冗余信息比较少(基于XML的外部DSL除外),而图形化的优点则在于直观,但它会连带很多冗余信息,在团队环境中使用图形化DSL会产生一些版本控制上的问题,因为这种图形化的DSL会夹带很多与显示相关的特性,比如图形位置、尺寸、颜色等等,而这些信息本不属于DSL的内容,但在版本控制系统中,又无法忽略对这些信息的更改操作,所以很容易增加解决版本冲突和代码合并的工作量。外部DSL的例子很多,例如:NHibernate的映射文件、web/app.config,虽然这两种是基于XML的;还有Entity Framework的图形化设计器,它是一种面向ORM(Object Relational Mapping,对象关系映射)领域的图形化DSL,它能通过T4将edmx转换成C#源代码,然后通过编译器联编并产生可执行程序。 接下来,我们将讨论如何在Visual Studio 2012中使用Visualization & Modeling SDK(VMSDK)开发DSL。通过VMSDK开发的DSL属于外部DSL。 使用Visual Studio 2012开发DSL的先决条件要使用Visual Studio 2012开发DSL,就需要安装Visual Studio Visualization & Modeling SDK(VMSDK),而安装VMSDK之前,需要确保Visual Studio SDK安装正确。VMSDK的前身是DSLTools,DSLTools是面向Visual Studio 2005/2008的DSL开发包。以下给出Visual Studio 2012 SDK和Visualization & Modeling SDK的下载地址:
在后面的案例中可以了解到,我们所开发的DSL会被打包成一个VSIX的Visual Studio扩展包,于是DSL的发布也变得异常简单:只需要在客户机上安装这个VSIX扩展包即可完成发布。这也是为什么VMSDK需要基于Visual Studio SDK的原因之一。 DSL解决方案的创建现在,我们使用Visual Studio来创建一个新的DSL解决方案,以便了解DSL的开发过程。在Visual Studio的New Project对话框中,在Other Project Types | Extensibility分类下,可以看到一个名为Domain-Specific Language Designer的工程模板,选择这个模板,并选择解决方案的保存路径,然后为解决方案设置一个名称,点击OK按钮: 现在可以看到Domain-Specific Language Designer Wizard对话框,这个向导对话框能帮助开发人员一步步地完成DSL解决方案的设置,它包含了以下这些内容:
在Solution Explorer中,我们可以看到,Visual Studio为DSL解决方案创建了两个工程:Dsl和DslPackage。Dsl是DSL开发的主要工程,今后 Dsl工程中比较重要的内容有:GeneratedCode文件夹、Resources文件夹以及DSL的定义文件:DslDefinition.dsl。GeneratedCode文件夹下包含了很多T4模板,这些T4模板会根据DslDefinition.dsl中DSL的定义产生相应的C#代码;Resources文件夹下则包含了DSL所使用的资源文件,比如用于在Visual Studio工具栏中表示某个工具的图标图片、在设计器中所使用的鼠标光标文件等等;我们开发DSL的大部分工作都是在DslDefinition.dsl文件上进行的,它指定了DSL包含了哪些领域类型(或者说领域概念)、这些领域类型之间的关系是什么,以及领域类型及关系在设计器上的展现形式。其它的两个文件不是我们讨论的重点,在常规的场景中无需深究这两个文件。 接下来,让我们了解一下使用Visual Studio进行DSL开发的集成开发环境。 DSL集成开发环境简介Visual Studio 2012 DSL集成开发环境主要由以下窗体构成:
在日常开发过程中,主要用到的也就是上面所介绍的四个窗体,另外我们还有可能需要根据实际情况来编写一些定制化的C#代码,所以C#程序编辑器和解决方案资源管理器(Solution Explorer)也会被用到。在下一个部分的介绍中,我们会经常涉及到Visual Studio 2012 DSL集成开发环境的这些内容。 DSL工具栏(Toolbox)简介DSL工具栏中常用的工具主要分为三类:领域类型(Domain Class)类工具、关系(Relationship)类工具以及图形(Shape)类工具,接下来让我们简单地了解一下这些常用的工具。 领域类型类工具领域类型类工具包括:Domain Class和Named Domain Class。Domain Class就是DSL的重要组成元素,它对DSL中某个特定的概念进行了准确的定义;Named Domain Class其实就是Domain Class,只不过它默认包含了一个字符串类型的领域属性:Name属性。在实际应用中使用所设计的DSL时,当我们创建一个Named Domain Class的实例时,就可以通过Name属性给这个实例设置一个名称。需要注意的是,当某个Domain Class继承于Named Domain Class时,无需再对Domain Class指定Name属性,此时Name属性将会被继承下来。 关系类工具关系类工具主要是用于指定领域类型之间的关系,这类工具包括:Embedding Relationship、Reference Relationship以及Inheritance。
图形类工具在实际应用中,我们可以使用某种图形来表示DSL中的概念。这些图形的形式是多样化的,比如在状态流DSL中,我们可以使用空心圆来表示起始状态,使用圆角矩形来表示中间状态,以及使用实心圆来表示结束状态。Visual Studio 2012 DSL集成开发环境为DSL开发人员提供了丰富的图形类工具,包括:Geometry Shape、Compartment Shape、Image Shape、Connector、Port Shape以及Swimline。
其它工具最后一个需要介绍的工具就是Diagram Element Map,它是一个连接工具,用于将DSL设计器中左边的领域类型/关系跟右边的图表元素(Diagram Element)连接起来,以表示领域类型/关系应该使用哪个图形进行展现。在开发DSL的时候,选择这个工具,然后用鼠标从领域类型/关系拖拽到相应的图表元素上,即可完成两者映射的建立。 使用Visual Studio 2012进行DSL开发调试在解决方案资源管理器中,Dsl工程上单击鼠标右键,选择Set as StartUp Project选项将其设置为启动工程,然后按F5键即可启动Visual Studio 2012 Experimental Instance,并对DSL进行调试。 在调试环境中,可以在“解决方案资源管理器”中看到两个DSL文件:Sample.mydsl1和Test.mydsl1,双击Sample.mydsl1即可在设计器中看到DSL的范例,而Test.mydsl1则是供开发人员测试用,开发人员可以在Test.mydsl1的设计器中,使用工具栏中的工具添加一些领域类型/关系的实例,以便对自己所开发的DSL进行测试。在下一个部分中,我们还会使用这个Experimental Instance来调试我们的客户化代码,并创建一些T4模板以实现自动化代码生成。 小结本文是《在Visual Studio 2012中使用VMSDK开发领域特定语言》专题文章的第一部分。本文首先对领域特定语言(DSL)进行了简单的介绍,包括DSL的概念、分类和例子;然后还讲解了Visual Studio 2012中开发DSL的先决条件、环境配置以及开发环境的窗口布局和工具栏等;最后简要地介绍了Visual Studio 2012下DSL的调试功能。在接下来的第二部分中,我们将从一个实际案例入手,介绍DSL开发的基本过程、客户化定制、T4自动化代码产生、DSL的部署,以及在生产环境中使用DSL等内容。 |
|