論面向組合子程序設計方法 之 創世紀


發現老庄的連載方法很好.又能吸引眼球又能好整以暇.於是從善如流. 


這幾天在完善我的neptune系統和jaskell語言。順手發現了一個logging的需求。如獲至寶阿。 

為什么呢?不是因為這個需求多么難,或者我的解決方法多么巧妙,而是因為,這個例子足夠簡單,直觀,要說明它,背景知識幾乎不大需要,三兩句話大家就明白需要達到什么效果。這種例子可不是隨便就想得到的。 

而同時,它又對實現提出了一定程度的靈活性要求,正好方便我展示我叫做“面向組合子”的程序設計方法。 


說到這里,不禁又有點沮喪,我也挺想象別人那樣,先高舉高打,玄之又玄,弄些哲學思辨,什么佛法呀,道德經阿,西游記亞,以及各位西方先哲的亟語,甚至量子力學的悖論。這樣才能吸引眼球,增加人氣呀。 
可是,等而下之的工匠氣作祟,說着說着就要拐到具體例子上去了。不爭氣呀。 


算了,不想那么多了。論道不是俺這種俗人所擅長的,還是鼓搗“器”吧。 


大致的背景是這樣: 
我的neptune是一個build system,在build的過程中會產生很多log信息。這些信息分為不同的重要級別。 

說到這里,肯定有人已經按奈不住:用Log4j! 

先不要急,我們這里不是要告訴你怎么處理你得程序中的logging需求,而是要通過這樣一個容易理解的例子來說明以下“面向組合子”的編程方法。所以,這里讓我們先假設我們不知道什么log4j。什么是log4j呀? 

當然,大致的思路總歸差不多的。因為我的neptune系統只需要一個logging的工具,而不關心這個logging工具是什么,這就是一個perfect的依賴注射的場合。 

先定義接口Logger,然后從構造函數傳遞近來一個Logger實例,接着就直接調用Logger就是了。 

Java代碼   收藏代碼
  1. public interface Logger{  
  2.   void print(int level, String msg);;  
  3.   void println(int level, String msg);;  
  4.   void logException(Throwable e);;  
  5. }  


print用來輸出信息,但是不折行,println可以折行。 
logException用來直接紀錄異常。這樣,對異常是直接printStackTrace,還是println(e.getMessage())就是由具體的Logger實現來決定,我的neptune只需要把遇到的異常報告給Logger就是了。 


好。接下來我吭哧吭哧地把neptune完成了,剩下的就是從哪里找一個Logger實現了。 

最簡單的Logger實現自然就是直接往屏幕上打印了: 

Java代碼   收藏代碼
  1. class SimpleLogger implements Logger{  
  2.   public void print(int lvl, String msg);{  
  3.     System.out.print(msg);;  
  4.   }  
  5.   public void println(int lvl, String msg);{  
  6.     System.out.println(msg);;  
  7.   }  
  8.   public void printException(Throwable e);{  
  9.     e.printStackTrace();;  
  10.   }  
  11. }  


直接把這個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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com