5.6 小结

祝贺你!用动态类型语言实现内部DSL的长篇讨论就要结束了。Ruby、Groovy和Clojure语言作为JVM平台语言多样性的代表,被我选为讲解用的实现语言。

JRuby是Ruby语言的Java实现,充当了Ruby语言与Java对象模型互操作的桥梁。它既有Ruby的强大元编程能力,又得益于Java的互操作性。Groovy语言本身就被当做一种Java DSL,与Java共用相同的对象模型。Clojure语言虽然也建立在Java的对象模型之上,却提供了Lisp那种强烈的函数式编程范式。

本章引导你使用以上三种语言实现了若干典型、现实的交易系统用例。Ruby的元编程能力强,可以使DSL在运行时保持动态,所以你可以组织建造高阶抽象。Groovy的运行时能力与Ruby相似,但它与Java的互操作更严丝合缝,毕竟两者共享同一个对象模型。

我们从第2章开始设计的指令处理DSL迎来了用Groovy语言实现的最终版本。通过这个例子,你应该了解了一般DSL的增量式演进的迭代过程。Clojure是运行在JVM上的Lisp语言,拥有出类拔萃的编译时元编程能力,也就是所谓的宏。你学习了如何运用宏来提高DSL的表现力和简洁度,并且知道它不会像其他语言的元对象协议那样增加运行时的负担。

最后,只要你能记住每个设计决策意味着哪些妥协和代价,肯定能做好DSL的设计。说到底,语言设计工作就是要检验你能不能在表现力和实现代价之间找好平衡。对于DSL来说,它充当着开发者和领域专家之间沟通渠道的角色,因此能充分传达代码的意图才是最根本的追求。

要点与最佳实践

  • 设计内部DSL时应该掌握所有的Ruby元编程手段。不要忘记元编程有代码复杂性和性能两方面的代价。

  • 优先选用Groovy Category代替ExpandoMetaClass来控制元编程的作用域。

  • Ruby的猴子补丁很吸引人,但它作用于全局命名空间。在DSL实现中使用猴子补丁时要三思而后行。

  • Clojure虽然在Java平台上实现,却是一种函数式语言。Clojure DSL的设计应该围绕领域函数进行。充分发挥函数式编程的长处,运用高阶函数和闭包来设计DSL的语义模型。

与三种最流行、最动态的JVM语言结伴同行的DSL设计之旅到了终点。经过这番历练,想必你已经对实现DSL的各种惯用手法有了基本的概念。能否选择符合语言习惯的正确实现方案,对开发工作有着决定性的影响,你选择的方案决定了DSL API的表现力水平。学习完本章,你对DSL开发的掌握程度又上了一个台阶,具备了深入探索Ruby、Groovy和Clojure语言各自实现技巧的基础。

下一章我们将跨过隔开类型系统两大阵营的那道篱笆,从另一端着眼,观察静态类型会塑造出什么样的DSL实现。等着你的还有充满趣味的练习——用Scala语言开发内部DSL。