台灣近年的 DDD (domain driven design) 研討會有蓬勃發展的趨勢,而 DDD 強調『軟體與問題領域 (problem domain) 充分結合』,這在我的經驗裡,是非常值得追求的特性。 DDD 應用的限制之一,是適用於大規模、複雜的軟體開發,如果對中小規模的問題應用 DDD ,可能還會適得其反。那如果我們想在中小規模的軟體開發裡,追求『軟體與問題領域充分結合』,有什麼技巧可以應用嗎?
資料驅動程式設計所闡述的編程技巧,可以透過加快迭代速度、還有讓軟體與問題領域 (problem domain) 充分結合,進而提高軟體開發人員的產出。
什麼是資料驅動程式設計?
資料驅動程式設計 (Data-driven programming) 一詞,出自 The Art of UNIX Programming 一書 。
它起始於一個觀察:早期的 Unix 程式設計師發現,人腦並不善長理解「循序邏輯」。相反的是,人腦比較容易快速掌握「資料」(註:此處的資料一詞,其語意應該是『可視資料』),怎樣算是資料呢?不管是表格、標記語言、巨集、樣板系統,這些都算是資料,都遠比循序邏輯容易理解。
於是,基於上述的洞見,Unix 程式設計師發展了一組工具與方法論集合,以讓程式碼可以被自動生成,同時,程式碼生成是由極小化的資料所指定。這組工具與方法論集合包含了:
工具:高階語言 (high-level language)
工具:領域專用語言 (domain specific language, DSL)
工具:程式碼產生器又或是直譯器 (code generator or interpreter)
方法論:資料驅動程式設計 (data driven programming)
1969 年的 Unix 程式設計師非常習慣於寫「語法解析器的規格」(lex & yacc) 來生成「語法解析器」以用來處理「類語言的配置文件」,因為他們相信設計完類語言的配置文件之後,剩下的工作就是對配置文件來做一般的「樹走訪」就可以完成了。要優雅地解決問題,需要利用資料驅動程式設計的兩個階段來達成,而其中第二個階段 (語法解析與樹走訪) 依賴於第一個階段 (配置文件設計) 之上。
在資料驅動程式設計的概念下,程式設計師開發出來的程式不僅只是可以運作的機器,而且是「可透過資料進行操作、配置的機器」。資料驅動程式設計的經典實作品是 awk。awk 語言就是一種領域專用語言、而 awk 程式就是直譯器,它可以理解使用者透過 awk 語言下達的高階指令,來達成使用者想要的文字處理任務。
高產出的關鍵
當某個特定的領域問題,可以利用資料驅動程式設計來處理,就可達成高產出,主要因為兩個原因:
高速迭代:由於領域問題可以使用領域專用語言來加以描述,而領域專用語言相對高階,需要寫的程式碼會大幅減少,迭代的速度自然可以加快。
軟體與領域問題充分結合:在極端的情況之下,甚至程式設計師日後就不再撰寫領域專用語言,直接交由領域專家來寫,因為領域專用語言往往(對於領域專家來說)相對簡單。
應用資料驅動程式設計
以下列舉一些,應用資料驅動程式設計的例子:
我曾經開發一個會計系統的 FIFO 演算法來計算存貨成本 (inventory cost)。原先別人的作法是用某程後端程式語言來做,於是完成的實作充滿了讀寫資料庫的程式碼。我主要依賴 SQL window function 與 SQL view 來實現相同的邏輯,新的實作不需寫讀寫資料庫的程式碼,因為它本來就在資料庫之內執行,總行數更縮短為 1/10。這個作法會有明顯的效果,因為資料庫提供的 SQL 本來就是一種 DSL,在純萃操作資料的使用情境,它遠比一般的後端程式語言更接近問題領域。
我需要處理一組資料集合,它是充滿了 m 對應 n 的關系,如果用 RDBMS 來建模的話,會有 20 張表,其中 10 張表是 domain entity table,另外 10 張表則是 bridge table 。而我改用 neo4j 來建模之後,全部只剩下 10 張表,因為圖學資料庫可以直接表現「邊」。這個作法會有明顯的效果,因為圖學資料庫提供比 RDBMS 提供更接近領域問題的建模方式,而這在關係很多的情況下,特別明顯。
ffmpeg
是一種高效的命令列影片處理程式,而需要處理的領域問題是:「要為 ffmpeg 包上一層圖形使用者介面,讓使用者不需要理解指令就可以來做影片的編輯。此外,圖形使用者介面要跟ffmpeg
解耦。」解決方案是:「設計一種 DSL 來描述各種使用者需要的ffmpeg
組合操作,同時寫一個 tree walk interpreter 來執行該 DSL ,這樣子,圖形使用者介面只需要可以產生前述的 DSL 即可。」這個作法會有明顯的效果,因為資料驅動程式設計可以把複雜度集中到 tree-walk interpreter 裡頭,帶來的好處是讓 DSL 部分變得極度簡化。
結論
資料驅動程式設計巧妙地結合了「加快迭代速度」與「讓軟體與問題領域充分結合」這兩種很正面的特性,可以有效提昇程式設計師產出。要應用資料驅動程式設計,可以有兩個不同的層次,簡易的層次,是選擇高階的工具來對問題領域建模,設法站在巨人的肩膀上;進階的層次,則是進而發展自己的建模工具,比方說 tree-walk interpreter。