4.1 充实DSL“工具箱”
工匠大师总是随身带着塞得满满的工具箱。箱里最开始的几件工具是他们从师傅那里继承来的,然后是其靠自己在一年一年的历练中逐渐充实进去的。这本书会交给你一些适合放进“箱子”里的DSL工具,尤其是实现内部DSL的工具。
我们接着2.3.1节讨论的内部DSL一般模式往下说。它们都是一些实现模式,适用于不同的DSL设计场景。2.3.1节用了不少代码片段来演示这些模式在常用语言中的呈现形式。本章将延续前面的讨论,举出金融中介系统这个问题域的例子,尝试将问题域的场景联系到它们在解答域的对应实现。阅读的时候,请留心收集可用于充盈你“工具箱”的工具吧。有时候,我会演示同一模式的不同实现手法,一般不同手法所用的实现语言也不同,重点说明每种手法涉及的权衡取舍。
展开讨论之前,我们先看看图4-2。图4-2中的模式还是我们在第2章看到的那些,但这次标注了每种模式对应的实现制品形态,也就是本章要讨论的内容。其中有一处标注需要说明,请看内嵌式DSL类别下的一个模式——“反射式元编程”方框。在本章的讨论中,我们会用这种模式实现Ruby和Groovy的隐式上下文(implicit context),还有Ruby的动态装饰器(dynamic decorator)。两种实现制品都有利于DSL服务于其用户:无需增加任何不必要的复杂性,用户就能清晰地表达意图。
图4-2 内部DSL实现模式及其制品示例。本章将讨论这些制品,并按图中所列语言提供相应的示例实现
如图4-2所示,内部DSL分为两类:
- 内嵌式DSL DSL寄身于宿主语言,这意味着所有的DSL代码都是程序员直接写出来的。
- 生成式DSL 部分DSL代码(大多为重复性的内容)由编译时或运行时语言机制生成。
在现实的应用中,模式不会单独出现。一般,多种模式协同作用,构成配套方案,合力解决用例场景。一种模式的作用产物被另一种模式所承接,整个系统环环相扣,就像“模式语言”在进行一场融洽对话。后面的讲解风格会按照DSL设计者的工作思路来安排。也就是说,我不打算像字典那样工整地分别介绍每种模式,而是先举出金融中介系统领域的DSL代码片段示例,然后剖析其中的模式结构。这样你不但能看到每种模式在现实用例中的呈现方式,还能体会不同模式结构如何协同形成更大的整体。
我们首先看看适用于实现内嵌式DSL的模式结构。