1 引言
这篇文章我将介绍多年来我使用DSL进行软件开发的最佳实践,在开始之前,我先概述一下内容。我专门针对自定义描述软件系统的领域特定语言,这些语言可以是文本的也可以是图形的,创建的模型再由代码生成,验证,模拟和解释使用,DSL是为开发人员和架构人员使用的(主要包括软件系统的架构/技术方面),同时也可以被那些通常不被称为开发者的商业用户使用。
我明确排除内置/嵌入式的DSL, 比如由Ruby,Coverge或者Lisp构建的DSL, 当然也不考虑MPS(译注:Jetbrains开发的,我在这里有介绍)这种通过扩展一个图灵完备的语言构建的DSL。
这篇文章是最佳实践的一些高度概括。对于他们其中的每一个,我都可以写很多页(实际上确实也是,已经有了很多关于这些或者其它实践的内容,见[1,2,3])。 尽管内容简单,这篇文章还是会提醒你当考虑MD*你的项目时应该考虑的所有的事情。
关于术语的注意事项:
使用MD*作为MDD,MDSD,MDE,MDA,MIC和其它原理相同的方法名词的缩写。
模型可以通过很多种方式处理,可以被验证,转换,生成代码或者解释。采用“模型处理(model processing)”(名词是模型处理器model processor)这个术语来代表所有的这些。
使用术语”元数据(metaware)”来指所有的元数据级别的物件。元数据包括DSL,元模型,编辑器,模型处理。
通常一个描述系统的全面的模型分成许多“模型单元(model units)”,我称之为分区partitions(例如XML文件)。
采用术语”业务应用business”,来指示所有应用领域,可以是科学,机械,自动化,金融或者保险,而不是具体的财务/会计/法律这些业务,这个词主要用来与程序员/设计师/分析师处理的程序/软件区分。
每一个最佳实践都评定了星级,数据来源于我在同事当中做的一个小调查。到现在为止,只有10个人回复了,所以这个调查不一定有代表性,但是它肯定是最佳实践的一个指示:
我并不认为是这样,我经常使用一种技术与之相矛盾
我没有使用过,但是它听起来有道理,我想如果当我必须要面对这样的问题的时候我会这样来做
我成功的使用过,但是我不肯定这是通用的最佳实践
我成功的使用过很多次,我确信这是最佳做法,不用才怪呢
文章有三个主要部分:
第1部分: DSL的设计,设计你的语言时一定要铭记的最佳实践。
第2部分: 模型处理,模型检查,解释和代码生成。
第3部分: 考虑你必须记住的有关过程和组织方面的事情。
第4部分: 着眼于MD*世界里面临的问题和挑战.
2 设计DSL
领域特定语言的语言来源
如何能够挖掘出你的DSL表示什么?如何来进行抽象,以及对应的符号是什么?这不是一个普通问题,事实是这是MD*里最主要的问题,这需要大量的经验和思考以及不断的迭代。不过也有一些典型的方法:
如果你在构建一个技术型的DSL,语言的来源通常是一个已经存在的框架Framework,类库library,架构architecture或者是架构模式architecture pattern,这方面的知识通常是你已经具备了的,构建DSL主要是形式化这些知识:定义表示方式,用格式化的语言表达出来,并且构建生成器生成部分实现代码,在这过程中,经常会将框架的一些功能特征当成合理的潜规则(默认规则),这能够提高框架的抽象层次,使框架更容易使用。
如果你在构建一个业务领域DSL, 你需要收集领域专家现有的知识,在保险,科学,后勤这些领域,专家们绝对有能力很好地表达相关知识.他们一直做这方面,经常使用Excel或者Word.他们有自己的”语言”来表达领域概念,虽然可能并不是规范的语言,并且没有工具支持。在这种情况下,你的工作就是提供这种规范格式和工具,除了领域知识,其它的东西也是可以利用的:比如硬件结构或者设备特征在个别领域也是不错的选择。
在以上两个领域,我们对DSL的期望很明确,只需要注意一些细节,表示形式,规范化,视点(ViewPoints),喜好(这些也是很重要的)。不过在其它的情况下我们就没有这么幸运了,如果没有现成的领域知识,我们必须去做一个领域分析,利用已有的应用,从需求出发,参与其中。
对于你的第一个DSL,尝试找到一两个案例,理想的情况是从第一个案例开始,因为构建DSL和支持工具的人往往就是领域专家或者软件架构师和开发者.
极根表现力
当构建DSL的时候,确认你没有被诱惑到构建又一个图灵完备的通用语言,其实在许多情况下,仅仅一个声明式的语言就足以表述一个系统了。
注意配置和定制之间的差异,一个可定制的DSL提供了足够的词汇量,让你能够创造性的组合成任意复杂的句子,可配置的DSL由明确的参数组成,用户可以直接指定参数的值(例如特征模型)。当然配置型DSL是比较有限的,因为实例以及事物之间的关系并不能够很容易的表达,然而它们并不是很复杂,你越是向配置方面倾斜,一般构建模型处理就越容易,对于用户来讲,也就更容易使用,因为它的表面的复杂性是有限的。
请注意精确性和算法完整之间的不同,许多领域专家能够正规的精确的定义他们的领域里的东西(即这个领域是什么),但是他们不能够定义算法来实现系统,但是作为开发者来说,你的工作就是提供一种形式语言来给领域专家用于表述事实,然后实现生成器和解析器来把这些事实映射为可执行的算法(要和它们表述的知识保持正确),DSL表述了“什么”,而模型处理器添加了“如何”。
如果需要关注你的系统由哪个3GL语言来抽象(例如,你需要一个图灵完备的强大的表达能力,而不用比较大的语义扩展),尝试定义一个DSL来实现这个并不是一个好的注意。定义或者生成一个API,来让开发者直接用3GL写代码, 你可以在生成的代码里直接生成挂钩,让开发者可以直接用3GL代码实现一些特殊的功能,保持挂钩的目的性一定要明确,其数量一定要有限!
符号,符号,符号
构建DSL的时候,表现符号是相当重要的。作为语言设计者,你关心的可能多半是基本的元数据模型,可能并不真正的关心”漂亮的语法”, 但是从领域用户的角度来看,情况可能正好相反。
特别是在业务领域(但不限于),要想成功,你必须调整你的表现方式来适应这个领域(甚至是这个领域已经存在的表现方式)。试图劝服他们来使用一个“更好的表现方式”往往会让你失望,只要实现他们有的就行了。
注意这可能需要文本或者图形符号,类似于Excel电子表格,表单系统或者他们的混合,现在的DSL工具在这方面有限制。不过我相信在今后几年DSL工具的演变中将主要解决这个问题。到现在为止,你只需要考虑到表现符号的易变性,尽最大努力开发可用的工具。
表现符号应该使通用的东西表达起来简明,能够提供一些合理的缺省规则,即使少数需要额外的修饰来实现也是允许的。
当勾画DSL定义原型的时候,从表现方式开始,与使用者直接交叉检查是很有用的。
图形 vs 文本符号
是不是图形描述的比文本描述的更容易理解呢? 其实不然。就易理解性来说最重要的就是把需要用语言抽象转达的概念排列,一个设计好的文本符号更加持久耐用,当然,对于一些特定类型的信息,图形符号是更好的选择:实体之间的关系,一些事件或者信号/数据流的时序/顺序。相反,用图形展现公式是一条走不通的死路(附注:图形公式编辑器在某种程度上是与显示分数,矩阵,积分等方式的混合体)。
在谈到决定一个合适的符号时,你可能需要考虑以下两个因素:大多数(但不是全部)工具环境,相对于比用户友好可扩展的图形编辑器来说,文本符号的编辑器(包括代码完成,语法高亮等)更容易构建和演变,文本符号还更容易与源代码管理和build机制集成。
此外,代替使用全面的图形编辑,你可能会考虑文本编辑+可视化图形(见下文)。
在使用图形编辑器的环境中需要做很多工作,我建议首先使用简单的文本编辑器(文本,树,通常的框/线)来稳定语言的概念和抽象,然后再投入开发一个完美的图形化编辑器。
在许多系统中,一些观点是支持图形编辑器,其它的是文本。有时候你甚至想混合这两种方式: 状态机(图形符号)其中的断言表达式(文本符号),但是这对于现在的工作来说比较棘手。
语义DSL(未评级)
只为DSL定义抽象和表现符号还是不够的,你还必须定义抽象的意义--语言的语义。
在某种意义上,一个语言的语义比使用这个语言表达需要更多的这个领域的知识:语言允许用户使用模型仅仅表达针对于某一特定 系统/应用/案例。然后语义需要这个领域每一个 系统/应用/案例 之间所有相同的东西。
从技术上讲,这是生成器,解析器的工作,平台使他们联系起来,然而从语言使用者的角度来说(他们并不知道什么是模型处理器),语义是隐性知识“语言是如何工作的”,而它必须解释为“语言的意义”.
有许多种正式地定义语义的方式,但是没有任何一个在主流DSL实践中是足够实用的(到2008年), 因为一种语言的含义在两个方面来定义:用短文来解释,用例子用于语言使用者,并且直接向下通过代码生成器或者解析器和可执行平台绑定在一起。(严格意义上讲,这是业务语义的定义,因为生成器映射语言概念到已经存在语义的目标语言概念)
原文: Best Practices for DSLs and Model-Driven Development
由于篇幅太长,所以分几部分翻译。翻译水平有限,如果英语不错,最好直接阅读原文.
向模型驱动开发的同学们强烈推荐此文!
作者:孤独侠客(似水流年)
出处:http://lonely7345.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。