文章目录
  1. 1. 学习曲线
  2. 2. 创建成本
  3. 3. 程序员的熟悉度
  4. 4. 与领域专家沟通
  5. 5. 与宿主语言混合
  6. 6. 强边界
  7. 7. 运行时配置
  8. 8. 趋于平庸
  9. 9. 组合多种DSL
  10. 10. 总结

根据之前的Blog,我们已经了解了内部DSL和外部DSL实现的细节,那下面我们就应该去仔细理解二者的优劣。这有助于在这两种技术之间做出更好的选择。

学习曲线

初看起来,内部DSL学习成本更小一些,而学习外部DSL,则需要学习解析器,文法和解析器生成器。同时大多数解析器生成器工具缺乏足够文档。

另一方面,内部DSL并不总如我们所想的那般容易。内部DSL常有赖于宿主语言中的一些晦涩技巧生成连贯接口,所以就算我们对语言熟悉,也需要花一定时间找到和学习这些技巧。

总体来说,内部DSL更容易学习。

创建成本

当谈及构建成本时,重要的是区分模型构建的成本和基于模型构建DSL层的成本。

就内部DSL而言,于模型之上构建一层“表达式生成器”就需要额外的付出。编写表达式生成器相对容易,但主要的工作并不只是让它们工作起来,而是润色语言,这样用起来才会比较舒服。

外部DSL,相应的成本在于解析器的创建。一旦掌握了“语法指导翻译”,编写文法和编译代码实际上是相当快的。

一旦熟悉了这些技术,构建成本相差无几。

程序员的熟悉度

使用内部DSL实在使用程序员熟悉的语言,使用起来更加容易。但是实际差别没有这么显著。诡异的连贯接口也需要花时间适应。

除了语法因素外,二者最大的区别在于工具。如果宿主语言本省有一个强大的IDE,那么通过内部DSL,依然可以使用这个IDE。外部DSL除了最基本的文本编辑功能外,可能一无所有。

与领域专家沟通

内部DSL通常和宿主语言的语法绑定在一起,导致其在表达自由度上有些受限,并且会有一些语法噪音。对于程序员这不是考虑的主要因素,但是对于领域专家却不是这样。

就算是最好的内部DSL,也无法提供和外部DSL一样的语法灵活度。

与宿主语言混合

内部DSL本质上只是一些连贯方法的使用约定,谁也无力阻止人们将DSL代码命令同命令式代码混合使用。DSL与宿主语言之间很薄的边界,可能有好处,也可能是问题,这取决于如何使用。

好处在于,当内部DSL缺乏某种构造时,我们可以很方便地使用宿主语言。

外部DSL无法和宿主语言混合使用,但我们可以把宿主语言作为“外加代码”嵌入DSL脚本。同样,DSL也可以作为字符串嵌入通用语言,类似于正则表达式和SQL一样。

总体来看,要混合使用宿主语言和DSL代码,内部DSL几乎总是最好的选择。

强边界

宿主语言代码和DSL代码混合并不总是带来好处,只有当DSL的用户非常熟悉宿主语言时,才会行得通。所以,有领域专家要阅读DSL时,这种混合就不合适啦。

如果DSL需要另一组程序员编写,这种混合也无益。DSL的好处就是其能力范围是有限制的,这种限制使其易于理解,屏蔽Bug。如果DSL有很强的边界,就会限制要测试的东西。如果用通用语言,万事皆有可能,我们就不得不通过约定和评审,盯紧边界。外部DSL的限制减少了我们需要关注的内容。

运行时配置

XML DSL之所以流行,很大一个原因是,改变代码执行上下文的时间从编译时到运行时。

一种方式是将解释型语言和编译型语言结合使用,然后,用解释型语言编写内部DSL。这种场景下,内部DSL的许多常见优势都虚弱了。除非项目组的大多数成员都熟悉动态语言。所以,在多数情况下,外部DSL结合静态宿主语言更合适。

趋于平庸

当代最成功的一种DSL是Ant,在巨大成功背后,隐藏这最大的问题是:随着时间的推移,其功能逐步增强,以致它不在拥有DSL的所需的受限表达性了。

对外部DSL,这种风险总是存在的–这个问题没有简单答案,需要持续关注以及决心,确保事情不会过于复杂。有一种替代方案是: 用其他语言解决复杂情况,引入另一种语言处理特殊以及复杂的情况。

内部DSL可以很好的和宿主语言融合,所以,没有这个问题。但可能有一个类似的问题,当与宿主语言交织在一起时,DSL就可能失去意义。

组合多种DSL

DSL应该是小巧、能力受限的。所以,要完成实际工作,就要将DSL同一种或多通用语言集成起来。也可以将多种DSL组合起来。

内部DSL同宿主语言混合很容易。

外部DSL,困难一些。用“外加代码”,但会显得笨拙。

总结

我们的结论就是没有结论。二者都有明显而通用的优势。可以考虑同时在两个方向尝试。

Glenn Vanderburg 发现有个方法很管用,当我们依然在试图理解DSL做什么的时候,先用内部DSL。一旦事情尘埃落定,当需要用到外部DSL的某些优势时,再构建一个也不迟。语义模型会让这个过程很简单。

文章目录
  1. 1. 学习曲线
  2. 2. 创建成本
  3. 3. 程序员的熟悉度
  4. 4. 与领域专家沟通
  5. 5. 与宿主语言混合
  6. 6. 强边界
  7. 7. 运行时配置
  8. 8. 趋于平庸
  9. 9. 组合多种DSL
  10. 10. 总结