簡介:Ward Cunningham 曾經說過,乾淨的代碼清晰地表達了代碼編寫者所 想要表達的東西,而優美的代碼則更進一步,優美的代碼看起來就像是專門為了 要解決的問題而存在的。在本文中,我們將展示一個組合式解析器的設計、實現 過程,最終的代碼是優美的,極具擴充性,就像是為瞭解析特定的文法而存在的 。我們還會選取 H.248 協議中的一個例子,用上述的組合式解析器實現其文法 解析器。讀者在這個過程中不僅能體會到代碼的美感,還可以學習到函數式編程 以及構建 DSL 的一些知識。
DSL 設計基礎
我們在用程式設計語言(比如:Java 語言)實現某項功能 時,其實就是在指揮電腦去完成這項功能。但是,語言能夠帶給我們的並不僅 僅局限於此。更重要的是,語言提供了一種用於組織我們的思維,表達計算過程 的架構。這個架構的核心就在於,如何能夠把簡單的概念組合成更為複雜的概念 ,同時又保持其對於組合方法的封閉,也就是說,組合起來的複雜概念對於組合 手段來說和簡單的概念別無二致。引用“Structure and Interpretation of Computer Programs”一書中的話來講,任何一個強大的語言都是通過 如下三種機制來達成這個目標的:
原子:語言中最簡單、最基本的實體;
組合手段:把原子組合起來構 成更複雜實體的方法;
抽象手段:命名複雜實體的方法,命名後的複雜 實體可以和原子一樣通過組合手段組合成為更複雜的實體。
像 Java 這 種通用的程式設計語言,由於其所關注的是解決問題的一般方法。因此,其所提供的 這三種機制也都是通用層面的。在解決特定問題時,這種通用的手段和特定問題 領域中的概念、規則之間是存在著語義鴻溝的,所以某些問題領域中非常清晰、 明確的概念和規則,在實現時就可能會變得不那麼清晰。作為程式員來說,用幹 淨的代碼實現出功能僅僅是初級的要求,更重要的是要提升通用語言的層次,構 建出針對特定問題領域的語言(DSL),這個過程中很關鍵的一點就是尋找並定 義出面向問題領域的 原子概念、組合的方法以及抽象的手段。這個 DSL 不一定 非得像通用語言那樣是完備的,其目標在於清晰、直觀地表達出問題領域中的概 念和規則,其結果就是把通用的程式設計語言變成解決特定問題的專用語言。
我們曾經在“基於 Java 的介面布局 DSL 的設計與實現”一 文中,構建了一種用於介面布局的 DSL,其中呈現了上述思想。在本文中,我們 將以解析器的構造為例,來講述如何構建一種用於字串解析的 DSL,這種 DSL 具有強大的擴充能力,讀者可以根據自己的需要定義出自己的組合手段。此外, 從本文中讀者還可以領略到 函數編程的優雅之處。