《领域特定语言》一3.8 DSL迁移

3.8 DSL迁移

DSL拥趸们应该警惕的一个风险是“先编写,后使用”的想法。同其他软件一样,成功的DSL需要不断演化。也就是说,以早期版本DSL编写的脚本可能无法在新版上运行。
DSL的诸多属性(无论好坏)同程序库完全一样,这一点也不例外。如果从别人那里得到一个程序库,基于它编写一些代码,他们升级了程序库,我们可能最终就卡在那里。DSL并不会真正改变这一点;本质上DSL定义的就是已发布接口(published interface),我们不得不自行处理这个后果。
在重构 [Fowler Refactoring]一书里,我开始使用已发布接口这个术语。已发布接口和更一般的“公共”接口的不同之处在于,前者由其他团队编写的代码使用。所以,修改已发布接口是很困难的,因为他们无法重写调用代码。修改已发布DSL对于内部DSL和外部DSL都是个问题。如果DSL尚未发布,而且相关语言有自动化重构工具,修改内部DSL还容易一点。
解决DSL修改问题的一种方式是,提供工具,自动把DSL从一个版本迁移到另一个版本。这些工具可以在升级时运行,也可以在尝试运行旧版脚本时自动运行。
有两种方式实现迁移。一种是增量迁移的策略,这种方式本质上同人们处理数据库设计的演化所采用的想法是一致的。我们会创建一个程序,对DSL定义的每个变化,都可以自动将旧版脚本迁移到新版本上。采用这种方式,当发布新版DSL时,还要提供一个脚本,迁移使用DSL的代码库。
对于增量迁移,有一点很重要,就是要让每一个修改尽可能小。想象一下,我们要从版本1升级到版本2,DSL定义有10处修改。在这种情况下,不要只创建一个“从版本1到版本2”的迁移脚本;相反,可以创建至少10个脚本。一次修改DSL定义的一个特性,为每个修改编写一个迁移脚本。将修改拆分为更多步骤,某些特性甚至要多于一步(因此要多于一个迁移脚本),你会发现这种做法非常有用。听起来,这种做法比单一脚本要多做一些工作,但重点在于,如果迁移都很小,脚本写起来会容易得多,而且把多个脚本串起来也很容易。所以,写10个脚本反而比写一个更快。
另一种方式是基于模型的迁移。这种策略可以与“语义模型”(第11章)配合使用。有了基于模型的迁移,我们的语言就可以支持多个解析器,每个发布版对应一个(当然,我们只会为版本1、2这样的大版本这样做,而不会理会中间步骤)。每个解析器都会组装语义模型。当采用语义模型时,解析器的行为相当简单。所以,有多个解析器也不是什么麻烦事。之后,针对所处理脚本的版本,可以运行相应的解析器。这种方式可以处理多个版本,但并不迁移脚本。为了处理迁移,可以根据语义模型编写一个生成器,生成DSL脚本。采用这种方式,可以对版本1的脚本运行解析器,组装语义模型,然后用生成器生成版本2的脚本。
基于模型的方式有一个问题,它很容易丢掉一些与语义无关但脚本编写者希望保留的东西。最明显的一个例子就是注释。如果解析器太过聪明,这个问题就会越发严重。以这种方式进行迁移的需求,会促使解析器写得更加简单,这是好事。
如果DSL的修改够大,很可能无法从版本1的脚本直接转换为版本2的语义模型。在这种情况下,可能需要保留版本1的模型(或者中间模型),给予它生成版本2脚本的能力。
对这两种方式,我没有很强烈的倾向。
迁移脚本可以由脚本程序员在需要的时候自己运行,或者让DSL系统自动运行。如果要自动运行的话,在脚本中记录它是哪个版本将会非常有用,这样,解析器可以很容易地检测到,并触发相应的迁移脚本。事实上,有一些DSL作者甚至强调说,DSL脚本上的版本信息是必需的,这样就可以更容易地检测出过时的DSL,以便进行脚本的迁移。虽然版本信息可能会给脚本添加一些噪音,但它基本上是不太会更改的东西。
当然,还有一个迁移选项,就是不迁移。保留版本1的解析器,让它组装版本2的模型。我们应该帮助人们进行迁移,如果他们需要更多特性,他们就会这么做。可以的话,直接支持旧脚本也未尝不可,这样,人们就可以按照自己的节奏去迁移。
虽然这样的技术很吸引人,但在实际中,是否值得这么做依然存疑。如我之前所说,这个问题同广为使用的程序库完全一样,自动迁移的策略没有多少人用。

时间: 2024-07-28 14:06:48

《领域特定语言》一3.8 DSL迁移的相关文章

《领域特定语言》一第2章 使用DSL 2.1定义DSL

第2章 使用DSL 看过上一章的例子后,即便尚未给出DSL的一般定义,对于何为DSL,你也应该已经有了自己的认识.(第10章中有更多例子.)现在,要开始讨论DSL的定义及其优势与问题.这样就可以为下一章讨论DSL实现提供一些上下文. 2.1定义DSL "领域特定语言"是一个很有用的术语和概念,但其边界很模糊.一些东西很明显是DSL,但另一些可能会引发争议.这一术语由来已久,不过,正如软件行业中的很多东西一样,它也从未有过一个确切的定义.然而,就本书而言,定义是非常有价值的.领域特定语言

如何设计一门编程语言(十) 正则表达式与领域特定语言(DSL)

几个月前就一直有博友关心DSL的问题,于是我想一想,我在gac.codeplex.com里面也创建了一些DSL,于是今天就来说一说这个事情. 创建DSL恐怕是很多人第一次设计一门语言的经历,很少有人一开始上来就设计通用语言的.我自己第一次做这种事情是在高中写这个傻逼ARPG的时候了.当时做了一个超简单的脚本语言,长的就跟汇编差不多,虽然每一个指令都写成了调用函数的形态.虽然这个游戏需要脚本在剧情里面控制一些人物的走动什么的,但是所幸并不复杂,于是还是完成了任务.一眨眼10年过去了,现在在写Gac

《领域特定语言》一导读

前 言 在我开始编程之前,DSL(Domain–Specific Language,领域特定语言)就已经成了程序世界中的一员.随便找个UNIX或者Lisp老手问问,他一定会跟你滔滔不绝地谈起DSL是怎么成为他的镇宅之宝的,直到你被烦得痛不欲生为止.但即便这样,DSL却从未成为计算领域的一大亮点.大多数人都是从别人那里学到DSL,而且只学到了有限的几种技术. 我写这本书就是为了改变这个现状.我希望通过本书介绍的大量DSL技术,让你有足够的信息来做出决策:是否在工作中使用DSL,以及选择哪一种DSL

《领域特定语言》一第1章 入 门 例 子1.1 哥特式建筑安全系统

第1章 入 门 例 子 落笔之初,我需要快速地解释一下本书的内容,就是解释什么是领域特定语言(Domain– Specific Language,DSL).为达此目的,我一般都会先展示一个具体的例子,随后再给出抽象的定义.因此,我会从一个例子开始,展示DSL可以采用的不同形式.在第2章里,我会试着把这个定义概括为一些更广泛适用的东西. 1.1 哥特式建筑安全系统 在我的童年记忆里,电视上播放的那些低劣的冒险电影是模糊却持久的.通常,这些电影的场景会安排某个古旧的城堡.密室或走廊在其中起着重要的作

《领域特定语言》一1.3 为格兰特小姐的控制器编写程序

1.3 为格兰特小姐的控制器编写程序 至此,我们已经实现了状态机模型,接下来,就可以为格兰特小姐的控制器编写程序了,如下所示: Event doorClosed = new Event("doorClosed", "D1CL"); Event drawerOpened = new Event("drawerOpened", "D2OP"); Event lightOn = new Event("lightOn&quo

《领域特定语言》一2.6 设计优良的DSL从何而来

2.6 设计优良的DSL从何而来 审核本书的人常常问我,有没有一些"设计优良语言"的技巧.毕竟,语言设计很特别,我们不希望这个世界上充斥着糟糕的语言.我想分享一些好的建议,但我坦白,真没什么好办法.如同任何写作一样,DSL的总体目标就是对读者要清晰.我们希望本书的典型读者,可能是程序员,抑或是领域专家,能够尽可能快速清晰地理解DSL里句子的意图.虽然我觉得可能无法告诉你如何做到这一点,但是我的确觉得在工作中牢记这一点非常有价值.总的说来,我是迭代设计的粉丝,这次也不例外.尽早从目标受众

《领域特定语言》一2.3DSL的问题

2.3DSL的问题 前面已经讨论了何时该采用DSL,接下来就该谈论什么时候不该采用DSL,或者至少是使用DSL应注意的问题.从根本上说,不使用DSL的唯一原因就是,在你的场景下,使用DSL得不到任何好处,或者,至少是DSL的好处不足以抵消构建它的成本.虽然DSL在有些场合下适用,但同样会带来一些问题.总的来说,我认为通常是高估了这些问题,一般人们不太熟悉如何构造DSL,以及DSL如何适应更为广阔的软件开发图景.还有,许多常提及的DSL问题混淆了DSL和模型,这也伤及了DSL的优势.许多DSL问题

《领域特定语言》一2.4 广义的语言处理

2.4 广义的语言处理 本书是关于领域专用语言的,但它也涉及语言处理技术.之所以二者重合,是因为在普通的开发团队里,用到语言处理技术的情况,90%都是为了DSL.但是,这些技术也可以用于其他方面,若不讨论这些情况,将是我的失职.我曾遇到过这方面一个很好的例子,那是在一次拜访ThoughtWorks项目团队时.他们有一个任务,要与某第三方系统通信,发送的消息以COBOL copybook定义.COBOL copybook是一种用来描述记录的数据结构格式.因为系统中有很多地方要用到,所以我的同事Br

《领域特定语言》一1.4 语言和语义模型

1.4 语言和语义模型 在这个例子之初,我谈到了构建一个状态机模型.这种模型的存在,以及它同DSL的关系,是至关重要的.在这个例子里,DSL的角色就是组装状态机模型.因此,当解析定制语法的版本时,遇到: events doorClosed D1CL 会创建一个新的事件对象(new Event("doorClosed","D1CL")),把它保存在一边(在一个 "符号表"(第14章)里),这样,遇到doorClosed=>active时,就可