天天看點

資料即代碼:元驅動程式設計

<a target="_blank" href="http://www.coolshell.cn">coolshell.cn</a>

幾個小夥伴在考慮下面這個各個語言都會遇到的問題:

問題:設計一個指令行參數解析api

一個好的指令行參數解析庫一般涉及到這幾個常見的方面:

1) 支援友善地生成幫助資訊

2) 支援子指令,比如:git包含了push, pull, commit等多種子指令

3) 支援單字元選項、多字元選項、标志選項、參數選項等多種選項和位置參數

4) 支援選項預設值,比如:–port選項若未指定認為5037

5) 支援使用模式,比如:tar指令的-c和-x是互斥選項,屬于不同的使用模式

經過一番考察,小夥伴們發現了這個幾個有代表性的api設計:

1. getopt():

getopt()的核心是一個類似printf的格式字元串的指令行參數描述串,如上面的”ac:d:”定義了”a”, “c”,”d”3個指令行參數,其中,a是一個标志符不需要參數,”c”和”d”需要跟參數。getopt()功能非常弱,隻支援單個字元的标志選項和參數選項。如果按上面的5點來比對,基本上隻能說是勉強支援第3點,其他幾項隻能靠程式自己來實作了,是以,想直接基于getopt()實作一個像git這樣複雜的指令行參數是不可能的,隻有自己來做很多的解析工作。小夥伴們看過getopt()之後一緻的評價是:圖樣圖森破。

2. google gflags

小夥伴們看了後不由得感歎“真心好用啊”!的确,gflags簡單地通過幾個宏就定義了指令行選項,基本上很好的支援了上面提到的1,3,4這幾項,比起getopt()來強多了。對于類似cp這樣的小指令,gflags應該是夠用了,但要達到git這種級别就顯得有些單薄了。

3. ruby commander

接下來小夥伴們又發現了ruby commander庫:

commander庫利用ruby酷炫的文法定義了一種描述指令行參數的内部dsl,看起來相當高端大氣上檔次。除了上面的第5項之外,其他幾項都有很好的支援,可以說commander庫的設計基本達到了git這種級别指令行參數解析的要求。隻是,要搞懂ruby這麼炫的文法和這個庫的使用方法恐怕就不如getopt()和gflags容易了。有小夥伴當場表示想要學習ruby,但是也有小夥伴表示再看看其他庫再說。

4. lisp cmdline庫

這是神馬浮雲啊?括号套括号,看起來很厲害的樣子,但又不是很明白。看到這樣的設計,有的小夥伴連評價都懶得評價了,但也有的小夥伴對lisp越發崇拜,表示lisp就是所謂的終極語言了,沒有哪門語言能寫出這麼不明覺曆的代碼來!小夥伴們正準備打完收工,突然…

5. node.js的lineparser庫

天啊!?這是什麼?我和小夥伴們徹底驚呆了!短短十幾行代碼就獲得了上面5點的全面支援,重要的是小夥伴們居然一下子就看懂了,沒有任何的遮遮掩掩和故弄玄虛。本來以為ruby和lisp很酷,小夥伴們都想馬上去學ruby和lisp了,看到這個代碼之後怎麼感覺前面全是在裝呢?有個小夥伴居然激動得哭着表示:我寫代碼多年,以為再也沒有什麼代碼可以讓我感動,沒想到這段代碼如此精妙,我不由得要贊歎了,實在是太漂亮了!

小夥伴們的故事講完了,您看懂了嗎?如果沒有看懂的話,正題開始了:

設計思想和具體技術的差別在于前者往往可以在不同的環境中以不同的形式展現出來。比如,熟悉函數式程式設計的程式員在了解了純函數的優點後即使是用c語言也會更傾向于寫出無副作用的函數來,這就是函數式思想在指令式環境的應用。是以,了解lisp思想一定要能在非lisp環境應用,才算是融彙貫通。

如果真正了解了lisp的本質,那所謂的“資料即代碼,代碼即資料”一點兒也不神秘,這不就是我們每天打交道的配置檔案嗎!?如果你還不是很了解的話,我們通過下面幾個問題慢慢分析:

1) 配置的本質是什麼?為什麼要在程式中使用配置檔案?

不知道你是否意識到了,我們每天都在使用的各種各樣的配置本質上是一種中繼資料也是一種dsl,這和lisp基于s表達式的“資料即代碼,代碼即資料”沒有本質差別。在c++、java等程式中引入配置檔案的目的正是用dsl彌補通用語言表達能力和靈活性的不足。我知道不少人喜歡從計算的角度來看到程式和語言,似乎隻有圖靈完備的語言如c++、java、python等才叫程式設計語言,而類似css和html這樣的東西根本不能叫做程式設計語言。其實,在我看來這種觀點過于狹隘,程式的本質是語義的表達,而語義表達不一定要是計算。

2) 配置是資料還是代碼?

很明顯,both!說配置是資料,因為它是聲明式的描述,能友善地修改和傳輸;說配置是代碼,因為它在表達邏輯,你的程式實際上就是配置的解釋器。

3) 配置的格式是什麼?

配置的格式是任意的,可以自己定義文法,隻要配以相應的解釋器就行。不過更簡單通用的做法是基于xml、json、或s表達式等标準結構,在此之上進一步定義schema。甚至完全不必是檔案,在我們的項目中配置經常是放到用關系資料庫中的。另外,下面我們還會看到用語言的literal資料作為配置。

4) 業務邏輯都可以放到配置中嗎?

這個問題的答案顯然是:yes!我沒有遇到過不可以放入配置的邏輯,隻是問題在于這樣做是否值得,能達到什麼效果。對于需要靈活變化,重複出現,有複用價值的東西放入作為配置是明智的選擇。這篇文章的主要目的就在于介紹把主要業務邏輯都放到配置中,再通過程式解釋執行配置的設計方法,我稱之為:元驅動程式設計(meta driven programming)。