红帽白皮书新鲜出炉!点击获取,让你的云战略更胜一筹! 了解详情
写点什么

DSL 的演进

  • 2010-05-18
  • 本文字数:4972 字

    阅读完需:约 16 分钟

简介

领域特定语言( DSL ) 是针对特定问题领域的编程语言,而非通用语言。要创建“不重复自己”(Don’t Repeat Yourself)、“业务用户可读”的代码,DSL 可是个好方法。在过去的几年里,有关 DSL 的文章比比皆是。

创建一种领域特定语言并非难事。但我们对领域的理解总是不断深入,要让 DSL 长期有用,我们就需要一种不断完善 DSL 的策略。如果你正在开发一个大型项 目,或是一条软件产品线( SPL ), 在很长一段时间内都需要使用 DSL 的话,那你最好考虑清楚该如何处理 DSL 的演进。

从借助版本化实现的向后兼容性,到语句的自动转换,本文将着眼于不断简化 DSL 演进过程的 DSL 构建方法。

避免问题

在第三代编程语言(3GL)的世界里,语言设计者非常清楚向后兼容性的重要性。无论 Java 的下一个版本会有哪些变化,都不太可能破坏先前版本中添加的任 何功能。不过使用 DSL 时,随着我们对问题空间的进一步探索,我们对领域的理解会发生彻底改变。在单独项目中,业务专家往往会在后期提出新的领域概念,这 就迫使你要追根溯源,重新考虑怎样才能最好地为领域建模。软件产品线里的每个新项目都会带来不同的需求,这些需求则会对 DSL 的优化设计产生影响。

过去,我们始终建议大家在进行领域特定建模(DSM)时,只有在业务规则频繁变化、而领域结构却相对稳定时再引入 DSL(两个条件分别是为了提高开发 DSL 及其相关工具的投资回报率,减少改进 DSL 结构时遇到的问题),从而减少这些问题。不过 DSL 现在应用得越来越广泛,理解与 DSL 演进相关的问题、 处理这些问题的一些策略就非常重要了。

问题是什么?

有些类型的 DSL 演进根本不是问题。如果你想增加一个新的领域概念,或是给概念新增一个可选特性【译注】,你只用扩展 DSL 语法就可以了,而这并不会破坏任何已有的代码。但在有些情况下,你必须考虑语法的这些变化会对已有的语句产生什么影响。这些情况有:

  • 删除一个概念或特性
  • 添加一个特性,不同语句需要的特性值可能会有所不同
  • 将特性连同其子特性转变为一个独立的概念
  • 增加一项新的约束,而已有的语句可能并不满足该约束

抽象语法(Abstract Grammar)vs. 具体句法(Concrete Syntax)

有一个重要的 DSL 概念能让接下来的讨论稍微简单一些,那就是抽象语法和具体句法之间的差异。DSL 的抽象语法描述了有效语句的结构,也包括所有相关的约 束。具体句法则描述了如何在 DSL 中正确编写语句的细节问题。

举例来说,假设有一个描述状态机的 DSL,它可能包括一个这样的概念:一个对象能有多个状态。抽象语法只能传达“一个对象能有多个状态”,(还有所有的约束,比如每个对象至少要有一个状态、给定对象的每个状态都应该有唯一的名称)。具体句法则可能是建模工具里的图、XML 文档、 Groovy Ruby 这些 DSL 本身的代码、电子表格、基于 DSL 的数据库 Schema,或者是自定义的文本句法。尽管在特定的具体句法中对 DSL 的某些内容作出改进会更具有挑战性(比如处理与图形化语言相关的位置和图形数据),但我们还是要着眼于抽象语法,来讨论大部分问题和 DSL 演进带来的影响,要明白表述语句的具体句法只是个次要问题。

进行 DSL 演进的方法

在已经有 DSL 语句的系统中,进行 DSL 演进的常见方法有三个:

  • 依靠向后兼容
  • 语言进行版本化
  • 自动进行语句转换

向后兼容性

解决问题最简单的方法就是回避问题。对 DSL 语法的修改坚决不能破坏已有语句。人们使用 DSL 一段时间后,往往会演进 DSL 语法,却不关心修改是否会破坏已有的语句。最终,用例会在这些演进的地方突然出现问题。

语言版本化

对于可能会破坏已有语句的 DSL 演进来说,最快捷的解决办法是对 DSL 进行版本化。无论你使用的是某种内部 DSL 的内置分析,还是“外部 DSL+ 显式的解 析器”(或许是 ANTLR Xtext ,也可能是使用 XML 具体语法时用到的 XML 解析器), 当你需要做重大更改时,你只用发布解析器的新版本、确保你的系统支持多版本,在语句需要利用后续版本中可用的扩展语法时,只要更新语句就可以了。这个方法 在一定程度上可以说是相当不错的,但对语言多版本进行维护、支持、调试的开销最终会变得难以承受。

语句转换自动化

理想的解决办法是能对 DSL 语句进行自动演进,只要你修改了语法,(如果可能)语句就可以自动更新。最简单的处理方法之一就是将转换应用到语法,然后使用 脚本语言或是 XSLT 、ATLAS 这种允许模型到模型( M2M )转换的语言编写脚本,将同样的改变运用于 DSL 语句。

转换示例

假设我们使用 XML 这一具体句法来描述应用中领域对象的 DSL。最初,我们可能有两个领域类——User 和 Product,如清单 1 所示。

清单 1:User 和 Product 领域对象的 XML 句法。

复制代码
<domainObject name="User" />
<domainObject name="Product" />

很快,我们决定添加 Property 的概念,而且每个 domainObject 可以有 0 到 n 个属性。这个转换并不会破坏现有的语句。它只涉及“添加概念” 和“添加可选关系”,也就是说,我们增加了一个新的概念——属性,以及一个新的可选关系(每个领域有 n 个属性,但属性并不是必需的)。清 单 2 是带有 Property 概念的语句:

清单 2:带有属性的领域对象。

复制代码
<domainObject name="User">
<properties>
<property name="FirstName" />
<property name="LastName" />
</properties>
</domainObject>
<domainObject name="Product">
<properties>
<property name="Title" />
<property name="Price" />
</properties>
</domainObject>

现在再添加一条——属性可以有一条验证规则。这样我 们就有了“添加可选特性”的转换,这里我们要为属性添加可选的“validationRule”特性。同样,由于特性是可选的,先前的语句仍然有效,所以 对语法应用了这一转换之后,我们并没有破坏当前的 DSL 语句,也就不需要对语句做什么处理了。

比方说我们就这样工作了一段时间,XML 最终就像清单 3 所显示的那样。

清单 3:带有验证规则的领域对象属性。

复制代码
<domainObject name="User">
<properties>
<property name="FirstName" />
<property name="LastName" validationRule="Required" />
</properties>
</domainObject>
<domainObject name="Product">
<properties>
<property name="Title" validationRule="maxlength=50"/>
<property name="Price" validationRule="isNumeric" />
</properties>
</domainObject>

不过现在我们意识到,有些情况下,我们要为一个属性 关联多个验证规则。这一问题有很多解决办法。让我们看看其中之一。首先,我们可以只改变 validationRule,使其成为以逗号分隔的验证规则列 表。这一变更不需要对现有语句进行任何修改(假设当前所有的验证规则中都没有逗号)。但语言中会出现容易让人误解的特性,因为 validationRule 支持以逗号分隔的规则列表并不是很容易理解。

接下来可能会采用的转换是“为特性重命名”。将 validationRule 重命名为 validationRuleList,你会有一个从语义上来说更有意义的特性名称。要做到这一点,你要有方法将这类转换应用于已有的语句。最佳方法因使 用的具体句法而不同,但 XML 具体句法(或是任何能与 XML 工程互相转换的内容)要做到这点还是相对容易的,这只是举了个例子。

我们继续扩展应用,不幸的是我们发现验证规则需要更复杂的参数。例如我们有一个规则,用户在网站上注册时密码和确认密码必须匹配。这个验证规则可以写成清单 4 所示的 XML 片段。

清单 4:带有密码和确认密码属性的验证规则。

复制代码
<validationRule name="PasswordMatchesConfirmation" type="propertyValuesMatch"
firstPropertyName="Password" secondPropertyName="PasswordConfirmation" />

我们现在的问题是要将“特性应用到关联的概念转换上 去”。让我们分析一下。首先,这个特性要“转换概念”,因为我们将 validationRule 作为属性使用,而现在要替换为单独的 validationRule 概念,这一概念在 XML 具体语法中用单独的 XML 元素表示。这个特性还要“转换为关联的概念”,因为我决定在 语言里用独立的片段来描述这些规则,而这些规能被不同的属性重用。举例来说,如果 FirstName 和 LastName 都是必需的属性,那它们就可以用同 一个“Required”验证规则。更适合这些情况的替代方法是使用“转换为组合概念的特性”——每个属性可以包含规则。

XML 片段使用“组合概念”的特性会是清单 5 所示的样子。

清单 5:带有“组合概念特性”的领域对象。

复制代码
<domainObject name="User">
<properties>
<property name="FirstName" />
<property name="LastName">
<validationRule name="Required" />
</property>
</properties>
</domainObject>
<domainObject name="Product">
<properties>
<property name="Title">
<validationRule name="maxlength" value="50" />
</property>
<property name="Price" validationRule="isNumeric">
<validationRule name="isNumeric" />
</property>
</properties>
</domainObject>

清单 6 显示了使用转换为关联概念的特性后,XML 的样子。

清单 6:带有“关联概念特性”的领域对象。

复制代码
<domainObject name="User">
<properties>
<property name="FirstName" />
<property name="LastName" validationRuleNameList="Required" />
</properties>
<validationRules>
<validationRule name="Required" />
</validationRules
</domainObject>
<domainObject name="Product">
<properties>
<property name="Title" validationRuleNameList="TitleMaxlength" />
<property name="Price" validationRuleNameList="isNumeric" />
<validationRules>
<validationRule name="TitleMaxlength" value="50" />
<validationRule name="isNumeric" />
</validationRules>
</properties>
</domainObject>

同样,这些转换都可以自动应用于已有的 DSL 语句。

自动化的局限性

当然,有一些转换是不能自动进行的。当你想应用“删除概念”或“删除特性”的转换时,你使用的工具很有可能会自动扫描已有的语句,但无论你是必须基于转换脚本提供的报告进行手工修改,还是不得不使用“deprecate”来代替“删除”转换,每当工具发现这些条目,不让你添加新条目、却又不会强迫你移除那些条目时,这些工具可能都会让你觉得相当痛苦。

同样的,如果你想用“添加必要特性”的转换为所有属性添加一个数据类型特性的话,除非你能给出缺省值(没特殊说明就是字符串)或一些智能的脚本规则,否则自动化工具最好能提供一个高效的 UI,能为历史语句填充所有的条目。

内部 DSL vs. 外部 DSL

认识到内部 DSL 的局限性是很重要的。在“最终用户可读性”方面,内部 DSL 提供了很多好处,不过对于那些使用某种语言内置 DSL 编写的语句来说,自动应用转换通常会比较棘手。内部 DSL 很好,但你要是在大型项目或软件产品线中广泛使用这些内部 DSL 的话,就要确保你要有一个将转换应用到这些 DSL 上的策略。否则在项目的生命周期里,为外部 DSL 创建工具所花的那点儿时间与使用内部 DSL 相比来说可能更划得来。

结论

本文最重要结论的是,只要你的 DSL 是成功的,那你最终会有许多使用这些 DSL 的语句。要真是这样,如果你确实需要演进你的 DSL,你最好是有一个处理这些语句转换的策略。

此外,认识到这个问题还没有解决也很重要。这一领域还需要很多工作要做,除了来自 MetaCase 的 MetaEdit+ 之外,大部分领域特定建模工具都不能很出色地处理元模型演进。

引用

关于作者

Peter Bell 是 SystemsForge 的 CEO 兼 CTO。他开发了生成自定义 Web 应用的软件产品线,该产品线融合了特征建模、产品线工程和领域特定建模。他的文章和演讲遍布全球,内容涉及领域 特定建模、代码生成、精益 / 敏捷开发,以及 Groovy 和 CFML 等 JVM 上运行的动态脚本语言。他的 Blog 为: http://appgen.pbell.com/

查看英文原文: DSL Evolution


译注:根据文中示例,attribute 在本文译为“特性”,property 译为“属性”。

感谢曹云飞对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2010-05-18 11:084878
用户头像

发布了 151 篇内容, 共 59.8 次阅读, 收获喜欢 18 次。

关注

评论

发布
暂无评论
发现更多内容

专场直播预约 | 邀您探讨KaiwuDB 离散制造业场景解决方案

墨天轮

数据库 解决方案 制造业 国产数据库 KaiwuDB

Perforce研讨会回顾 | Helix Core在芯片行业的应用实例:芯片项目的版本控制、持续集成及自动化

龙智—DevSecOps解决方案

ci cicd 版本控制 持续集成 芯片开发

GPT-4:我不是来抢你饭碗的,我是来抢你锅的

这我可不懂

低代码 ChatGPT JNPF GPT-4

代码质量与安全 | 免费的静态分析工具好吗?

龙智—DevSecOps解决方案

SAST 静态代码扫描 DAST

代码实战带你了解深度学习中的混合精度训练

华为云开发者联盟

人工智能 深度学习 华为云 华为云开发者联盟 企业号 3 月 PK 榜

通通透透看无服务器计算:由来、场景和问题

天翼云开发者社区

听说火山引擎推出的DataLeap,已经可以支持万级表的数据血缘图谱了!

字节跳动数据平台

大数据 数据治理 数据研发 企业号 3 月 PK 榜

深入理解关键字volatile

小小怪下士

Java 程序员 volatile 关键字

AI笔刷怎样导入?adobe ai笔刷安装教程

Rose

AI画笔 AI教程 Illustrator 2023 下载 AI中文版

MQTT 5.0特性Inflight Window&Message Queue

EMQ映云科技

物联网 IoT mqtt emqx 企业号 3 月 PK 榜

Portraiture最新版插件新增哪些功能?

茶色酒

Portraiture4

Go Slice 扩容的这些坑你踩过吗?

王中阳Go

Go golang 高效工作 学习方法 面试题

共铸国云智领未来| 以数字林草之“笔” 绘就塞上江南新图景

天翼云开发者社区

全球掀起AI热,天翼云智算能力已就绪!

天翼云开发者社区

Ascend CL两种数据预处理的方式:AIPP和DVPP

华为云开发者联盟

人工智能 华为云 昇腾CANN 华为云开发者联盟 企业号 3 月 PK 榜

PostgreSQL:psql 介绍

天翼云开发者社区

第四朵“云”!全托管的时序数据云平台 TDengine Cloud 正式支持阿里云

TDengine

大数据 tdengine 阿里云 时序数据库 云服务

开启一个A/B实验到底有多简单?

字节跳动数据平台

云服务 AB testing实战 ab测试 企业号 3 月 PK 榜

Atlassian Server用户新选择 | 迁移到数据中心版前,您需要做这些准备(1)

龙智—DevSecOps解决方案

Atlassian Atlassian迁移 数据中心版 server版

GPT-4:不open的OpenAI,终于不再编造事实

鼎道智联

openai ChatGPT4

EMQ&南洋万邦云边一体化方案:激活数据潜力,打造智慧工业园区

EMQ映云科技

物联网 IoT 工业互联网 智能制造 企业号 3 月 PK 榜

云计算搭上“双碳”,天翼云在绿色算力赛道加速跑

天翼云开发者社区

盘活存储资源,天翼云HBlock助力企业绿色高效发展!

天翼云开发者社区

简单小巧的右键助手:MouseBoost for Mac让您的工作效率大幅度提高

Rose

mac效率工具 右键助手 MouseBoost激活版

CorelDRAW Graphics Suite2023功能介绍

茶色酒

cdr2023

大语言模型必将取代一切?暂时不会!

深数

人工智能 科技 AGI GPT LLM

Neural Filters神经滤镜插件如何安装?PS神经滤镜插件安装教程

Rose

mac系统 Neural Filters PS滤镜插件 PS20221下载

StyleGAN 生成 AI 虚拟人脸,再也不怕侵犯肖像权

极客飞兔

人工智能 AI 图像处理 StyleGAN 人脸生成

Linux进程学习【进程地址】

Yohifo

Linux 学习 运维 后端 进程

如何利用ChatGPT搞科研?

Openlab_cosmoplat

人工智能 开源社区 ChatGPT

币安欧意交易所合约跟单平台软件开发详情(api对接)

开发微hkkf5566

DSL的演进_架构_Peter Bell_InfoQ精选文章