简介:Ward Cunningham 曾经说过,干净的代码清晰地表达了代码编写者所 想要表达的东西,而优美的代码则更进一步,优美的代码看起来就像是专门为了 要解决的问题而存在的。在本文中,我们将展示一个组合式解析器的设计、实现 过程,最终的代码是优美的,极具扩展性,就像是为了解析特定的语法而存在的 。我们还会选取 H.248 协议中的一个例子,用上述的组合式解析器实现其语法 解析器。读者在这个过程中不仅能体会到代码的美感,还可以学习到函数式编程 以及构建 DSL 的一些知识。
DSL 设计基础
我们在用编程语言(比如:Java 语言)实现某项功能 时,其实就是在指挥计算机去完成这项功能。但是,语言能够带给我们的并不仅 仅局限于此。更重要的是,语言提供了一种用于组织我们的思维,表达计算过程 的框架。这个框架的核心就在于,如何能够把简单的概念组合成更为复杂的概念 ,同时又保持其对于组合方法的封闭,也就是说,组合起来的复杂概念对于组合 手段来说和简单的概念别无二致。引用“Structure and Interpretation of Computer Programs”一书中的话来讲,任何一个强大的语言都是通过 如下三种机制来达成这个目标的:
原子:语言中最简单、最基本的实体;
组合手段:把原子组合起来构 成更复杂实体的方法;
抽象手段:命名复杂实体的方法,命名后的复杂 实体可以和原子一样通过组合手段组合成为更复杂的实体。
像 Java 这 种通用的编程语言,由于其所关注的是解决问题的一般方法。因此,其所提供的 这三种机制也都是通用层面的。在解决特定问题时,这种通用的手段和特定问题 领域中的概念、规则之间是存在着语义鸿沟的,所以某些问题领域中非常清晰、 明确的概念和规则,在实现时就可能会变得不那么清晰。作为程序员来说,用干 净的代码实现出功能仅仅是初级的要求,更重要的是要提升通用语言的层次,构 建出针对特定问题领域的语言(DSL),这个过程中很关键的一点就是寻找并定 义出面向问题领域的 原子概念、组合的方法以及抽象的手段。这个 DSL 不一定 非得像通用语言那样是完备的,其目标在于清晰、直观地表达出问题领域中的概 念和规则,其结果就是把通用的编程语言变成解决特定问题的专用语言。
我们曾经在“基于 Java 的界面布局 DSL 的设计与实现”一 文中,构建了一种用于界面布局的 DSL,其中呈现了上述思想。在本文中,我们 将以解析器的构造为例,来讲述如何构建一种用于字符串解析的 DSL,这种 DSL 具有强大的扩展能力,读者可以根据自己的需要定义出自己的组合手段。此外, 从本文中读者还可以领略到 函数编程的优雅之处。