论面向组合子程序设计方法 之 创世纪
生活随笔
收集整理的這篇文章主要介紹了
论面向组合子程序设计方法 之 创世纪
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
發現老莊的連載方法很好.又能吸引眼球又能好整以暇.于是從善如流.?
這幾天在完善我的neptune系統和jaskell語言。順手發現了一個logging的需求。如獲至寶阿。?
為什么呢?不是因為這個需求多么難,或者我的解決方法多么巧妙,而是因為,這個例子足夠簡單,直觀,要說明它,背景知識幾乎不大需要,三兩句話大家就明白需要達到什么效果。這種例子可不是隨便就想得到的。?
而同時,它又對實現提出了一定程度的靈活性要求,正好方便我展示我叫做“面向組合子”的程序設計方法。?
說到這里,不禁又有點沮喪,我也挺想象別人那樣,先高舉高打,玄之又玄,弄些哲學思辨,什么佛法呀,道德經阿,西游記亞,以及各位西方先哲的亟語,甚至量子力學的悖論。這樣才能吸引眼球,增加人氣呀。?
可是,等而下之的工匠氣作祟,說著說著就要拐到具體例子上去了。不爭氣呀。?
算了,不想那么多了。論道不是俺這種俗人所擅長的,還是鼓搗“器”吧。?
大致的背景是這樣:?
我的neptune是一個build system,在build的過程中會產生很多log信息。這些信息分為不同的重要級別。?
說到這里,肯定有人已經按奈不住:用Log4j!?
先不要急,我們這里不是要告訴你怎么處理你得程序中的logging需求,而是要通過這樣一個容易理解的例子來說明以下“面向組合子”的編程方法。所以,這里讓我們先假設我們不知道什么log4j。什么是log4j呀??
當然,大致的思路總歸差不多的。因為我的neptune系統只需要一個logging的工具,而不關心這個logging工具是什么,這就是一個perfect的依賴注射的場合。?
先定義接口Logger,然后從構造函數傳遞近來一個Logger實例,接著就直接調用Logger就是了。?
Java代碼??public?interface?Logger{?? ??void?print(int?level,?String?msg);;?? ??void?println(int?level,?String?msg);;?? ??void?logException(Throwable?e);;?? }??
print用來輸出信息,但是不折行,println可以折行。?
logException用來直接紀錄異常。這樣,對異常是直接printStackTrace,還是println(e.getMessage())就是由具體的Logger實現來決定,我的neptune只需要把遇到的異常報告給Logger就是了。?
好。接下來我吭哧吭哧地把neptune完成了,剩下的就是從哪里找一個Logger實現了。?
最簡單的Logger實現自然就是直接往屏幕上打印了:?
Java代碼??class?SimpleLogger?implements?Logger{?? ??public?void?print(int?lvl,?String?msg);{?? ????System.out.print(msg);;?? ??}?? ??public?void?println(int?lvl,?String?msg);{?? ????System.out.println(msg);;?? ??}?? ??public?void?printException(Throwable?e);{?? ????e.printStackTrace();;?? ??}?? }??
直接把這個SimpleLogger注射進我的neptune,整個系統就可以工作了。?
no big deal,對么??
好了,下面我們開始真正實現完整的logging系統了。經過分析,我們大致有以下的需要:?
[list]1。logger可以把信息打印到log文件中。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進入error.log,什么進入warning.log,什么進入info.log也需要決定。?
3。也許可以象ant一樣把所有的信息都打印到一個文件中。?
4。每條logging信息是否要同時打印當前的系統時間?也是一個需要抉擇的問題。?
5。不僅僅是log文件,我們還希望能夠在標準錯誤輸出上直接看見錯誤,普通的信息可以打印到log文件中,對錯誤信息,我們希望log文件和標準輸出上都有。?
6。標準輸出上的東西只要通知我們出錯了就行,大概不需要詳細的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個簡短的exception的message就夠了。?
7。warning似乎也應該輸出到屏幕上。?
8。不管文件里面是否要打印當前系統時間,屏幕上應該可以選擇不要打印時間。?
9。客戶應該可以通過命令行來決定log文件的名字。?
10。客戶可以通過命令行來決定log的細節程度,比如,我們只關心info一樣級別的信息,至于debug, verbose的信息,對不起,不要打印。?
11。neptune生成的是一些Command對象,這些對象運行的時候如果出現exception,這些exception會帶有execution trace,這個execution trace可以告訴我們每個調用棧上的Command對象在原始的neptune文件中的位置(行號)。?
這種exception叫做NeptuneException,它有一個printExecutionTrace(PrintWriter)的方法來打印execution trace。?
所以,對應NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調用printExecutionTrace()。?
12。neptune使用的是jaskell語言,如果jaskell腳本運行失敗,一個EvaluationException會被拋出,這個類有一個printEvaluationTrace(PrintWriter)的方法來打印evaluation trace,這個trace用來告訴我們每個jaskell的表達式在腳本文件中的位置。?
所以,對應EvaluationException,我們要在printStackTrace()之前,調用printEvaluationTrace()。?
13。execution trace和evaluation trace應該被打印到屏幕上和log文件兩個地方。?
14。因為printExecutionTrace()和printEvaluationTrace()本身已經打印了這個異常的getMessage(),所以,對這兩種異常,我們就不再象對待其它種類的異常那樣在屏幕上打印getMessage()了,以免重復。?
15。也許還有一些暫時沒有想到的需求, 比如不是寫入log文件,而是畫個圖之類的變態要求。[/list:u]?
大致上,我目前遇到的需求也就是這些了。?
好了,允許我賣個關子,下回分解的時候再說怎么用“面向組合子”和依賴注射的方法來解決這個問題吧。?
在本節結束之前,我稍微提一下“面向組合子”的來歷。?
組合子,英文叫combinator,是函數式編程里面的重要思想。如果說OO是歸納法(分析歸納需求,然后根據需求分解問題,解決問題),那么“面向組合子”就是“演繹法”。通過定義最基本的原子操作,定義基本的組合規則,然后把這些原子以各種方法組合起來。我最近一段時間做的東西,jaskell不用說了,函數式語言。yan, neptune, jparsec全是用面向組合子的思想開發的。?
OO就像是猜謎,給你一個蘋果,然后問你:這個蘋果是怎么得到的呢?然后你分析一番,說:我認為這個蘋果是由分子組成的,這些分子如此這般排列,然后分子又由原子組成,如此這般排列...?
而CO(面向組合子),就等于是說:這有H, C, O三種原子,強弱兩種作用力,你來看看能做點什么出來吧,然后你就像搭積木一樣,把這三種原子,兩種作用力搭建出這大千世界,什么毛毛蟲,狗熊,周星星,不小心,一下就做出了一個蘋果。?
OO的關鍵是需求。?
所謂"refactor",不過也是強調需求,讓你不要自作聰明地瞎假設需求而復雜化設計。時刻著眼于當前的需求。這樣,一旦需求變更,所浪費的力氣可以保證最小,而且,船小才好調頭嘛。?
如果需求分析的不好,一切就歇菜了,雖然因為一些比如ioc之類的設計方法能讓你不至于推到重來,但是需求仍然是重中之重。?
那些什么上下文沒有,上來就說“怎么用OO來做一個人騎車呀?”,“是人.騎(車)呀?還是車.被騎(人)?”純粹是沒頭沒腦地瞎掰。?
而CO的關鍵則是組合子和組合規則的設計。這些組合方法必須非常精巧,盡量正交。組合子的設計既要簡單(越簡單才越容易被組合),還要完整。?
比如說,對整數這個組合子,我們有+-*等組合方法,這樣只要有了0,1這兩個組合子,我們就可以構造出整個整數世界。?
可是,精巧的組合子設計也不是那么容易的。需要有一點點數學的感覺和嚴密的邏輯思維基礎。?
有人說,上帝是用OO設計的世界,可要我是上帝,我寧可用CO。?
設計幾個簡單的基本粒子,幾個簡單的相互作用力,然后讓這些東西自己組合,隨意發展,不是比事必躬親,先想透徹了自己想讓世界是什么樣子,然后一張一張圖紙地具體設計,一個一個人地造,?
“撒旦,你去干壞事,往死了整這幫賤人!”?
“天使,你去干好事,打個巴掌再給他們點甜棗吃。”?
“兒子,你下去混混,看丫敢不敢釘死你!”?
來的容易美妙??
這幾天在完善我的neptune系統和jaskell語言。順手發現了一個logging的需求。如獲至寶阿。?
為什么呢?不是因為這個需求多么難,或者我的解決方法多么巧妙,而是因為,這個例子足夠簡單,直觀,要說明它,背景知識幾乎不大需要,三兩句話大家就明白需要達到什么效果。這種例子可不是隨便就想得到的。?
而同時,它又對實現提出了一定程度的靈活性要求,正好方便我展示我叫做“面向組合子”的程序設計方法。?
說到這里,不禁又有點沮喪,我也挺想象別人那樣,先高舉高打,玄之又玄,弄些哲學思辨,什么佛法呀,道德經阿,西游記亞,以及各位西方先哲的亟語,甚至量子力學的悖論。這樣才能吸引眼球,增加人氣呀。?
可是,等而下之的工匠氣作祟,說著說著就要拐到具體例子上去了。不爭氣呀。?
算了,不想那么多了。論道不是俺這種俗人所擅長的,還是鼓搗“器”吧。?
大致的背景是這樣:?
我的neptune是一個build system,在build的過程中會產生很多log信息。這些信息分為不同的重要級別。?
說到這里,肯定有人已經按奈不住:用Log4j!?
先不要急,我們這里不是要告訴你怎么處理你得程序中的logging需求,而是要通過這樣一個容易理解的例子來說明以下“面向組合子”的編程方法。所以,這里讓我們先假設我們不知道什么log4j。什么是log4j呀??
當然,大致的思路總歸差不多的。因為我的neptune系統只需要一個logging的工具,而不關心這個logging工具是什么,這就是一個perfect的依賴注射的場合。?
先定義接口Logger,然后從構造函數傳遞近來一個Logger實例,接著就直接調用Logger就是了。?
Java代碼??
print用來輸出信息,但是不折行,println可以折行。?
logException用來直接紀錄異常。這樣,對異常是直接printStackTrace,還是println(e.getMessage())就是由具體的Logger實現來決定,我的neptune只需要把遇到的異常報告給Logger就是了。?
好。接下來我吭哧吭哧地把neptune完成了,剩下的就是從哪里找一個Logger實現了。?
最簡單的Logger實現自然就是直接往屏幕上打印了:?
Java代碼??
直接把這個SimpleLogger注射進我的neptune,整個系統就可以工作了。?
no big deal,對么??
好了,下面我們開始真正實現完整的logging系統了。經過分析,我們大致有以下的需要:?
[list]1。logger可以把信息打印到log文件中。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進入error.log,什么進入warning.log,什么進入info.log也需要決定。?
3。也許可以象ant一樣把所有的信息都打印到一個文件中。?
4。每條logging信息是否要同時打印當前的系統時間?也是一個需要抉擇的問題。?
5。不僅僅是log文件,我們還希望能夠在標準錯誤輸出上直接看見錯誤,普通的信息可以打印到log文件中,對錯誤信息,我們希望log文件和標準輸出上都有。?
6。標準輸出上的東西只要通知我們出錯了就行,大概不需要詳細的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個簡短的exception的message就夠了。?
7。warning似乎也應該輸出到屏幕上。?
8。不管文件里面是否要打印當前系統時間,屏幕上應該可以選擇不要打印時間。?
9。客戶應該可以通過命令行來決定log文件的名字。?
10。客戶可以通過命令行來決定log的細節程度,比如,我們只關心info一樣級別的信息,至于debug, verbose的信息,對不起,不要打印。?
11。neptune生成的是一些Command對象,這些對象運行的時候如果出現exception,這些exception會帶有execution trace,這個execution trace可以告訴我們每個調用棧上的Command對象在原始的neptune文件中的位置(行號)。?
這種exception叫做NeptuneException,它有一個printExecutionTrace(PrintWriter)的方法來打印execution trace。?
所以,對應NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調用printExecutionTrace()。?
12。neptune使用的是jaskell語言,如果jaskell腳本運行失敗,一個EvaluationException會被拋出,這個類有一個printEvaluationTrace(PrintWriter)的方法來打印evaluation trace,這個trace用來告訴我們每個jaskell的表達式在腳本文件中的位置。?
所以,對應EvaluationException,我們要在printStackTrace()之前,調用printEvaluationTrace()。?
13。execution trace和evaluation trace應該被打印到屏幕上和log文件兩個地方。?
14。因為printExecutionTrace()和printEvaluationTrace()本身已經打印了這個異常的getMessage(),所以,對這兩種異常,我們就不再象對待其它種類的異常那樣在屏幕上打印getMessage()了,以免重復。?
15。也許還有一些暫時沒有想到的需求, 比如不是寫入log文件,而是畫個圖之類的變態要求。[/list:u]?
大致上,我目前遇到的需求也就是這些了。?
好了,允許我賣個關子,下回分解的時候再說怎么用“面向組合子”和依賴注射的方法來解決這個問題吧。?
在本節結束之前,我稍微提一下“面向組合子”的來歷。?
組合子,英文叫combinator,是函數式編程里面的重要思想。如果說OO是歸納法(分析歸納需求,然后根據需求分解問題,解決問題),那么“面向組合子”就是“演繹法”。通過定義最基本的原子操作,定義基本的組合規則,然后把這些原子以各種方法組合起來。我最近一段時間做的東西,jaskell不用說了,函數式語言。yan, neptune, jparsec全是用面向組合子的思想開發的。?
OO就像是猜謎,給你一個蘋果,然后問你:這個蘋果是怎么得到的呢?然后你分析一番,說:我認為這個蘋果是由分子組成的,這些分子如此這般排列,然后分子又由原子組成,如此這般排列...?
而CO(面向組合子),就等于是說:這有H, C, O三種原子,強弱兩種作用力,你來看看能做點什么出來吧,然后你就像搭積木一樣,把這三種原子,兩種作用力搭建出這大千世界,什么毛毛蟲,狗熊,周星星,不小心,一下就做出了一個蘋果。?
OO的關鍵是需求。?
所謂"refactor",不過也是強調需求,讓你不要自作聰明地瞎假設需求而復雜化設計。時刻著眼于當前的需求。這樣,一旦需求變更,所浪費的力氣可以保證最小,而且,船小才好調頭嘛。?
如果需求分析的不好,一切就歇菜了,雖然因為一些比如ioc之類的設計方法能讓你不至于推到重來,但是需求仍然是重中之重。?
那些什么上下文沒有,上來就說“怎么用OO來做一個人騎車呀?”,“是人.騎(車)呀?還是車.被騎(人)?”純粹是沒頭沒腦地瞎掰。?
而CO的關鍵則是組合子和組合規則的設計。這些組合方法必須非常精巧,盡量正交。組合子的設計既要簡單(越簡單才越容易被組合),還要完整。?
比如說,對整數這個組合子,我們有+-*等組合方法,這樣只要有了0,1這兩個組合子,我們就可以構造出整個整數世界。?
可是,精巧的組合子設計也不是那么容易的。需要有一點點數學的感覺和嚴密的邏輯思維基礎。?
有人說,上帝是用OO設計的世界,可要我是上帝,我寧可用CO。?
設計幾個簡單的基本粒子,幾個簡單的相互作用力,然后讓這些東西自己組合,隨意發展,不是比事必躬親,先想透徹了自己想讓世界是什么樣子,然后一張一張圖紙地具體設計,一個一個人地造,?
“撒旦,你去干壞事,往死了整這幫賤人!”?
“天使,你去干好事,打個巴掌再給他們點甜棗吃。”?
“兒子,你下去混混,看丫敢不敢釘死你!”?
來的容易美妙??
哈。終于形而上起來了,爽!
from:http://ajoo.iteye.com/blog/23303
總結
以上是生活随笔為你收集整理的论面向组合子程序设计方法 之 创世纪的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java中的工厂模式
- 下一篇: 论面向组合子程序设计方法 之 失乐园 之