面向组合子程序设计方法 之 新约
生活随笔
收集整理的這篇文章主要介紹了
面向组合子程序设计方法 之 新约
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
每個(gè)小孩剛開(kāi)始走路的時(shí)候都是跌跌撞撞的。?
我們不自量力,妄圖踩著上帝的步伐前進(jìn)。結(jié)果就是這么幾個(gè)簡(jiǎn)單的象白開(kāi)水似的類(lèi)。失望嗎?是不是造物試圖模仿造物主本身就是一種可笑的狂妄呢??
別急,讓我們失聲痛哭之前先看看我們這幾步走的是不是一錢(qián)不值。?
[list]?
1。logger可以把信息打印到log文件中。 ?
容易,直接創(chuàng)建一個(gè)WriterLogger就好了。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進(jìn)入error.log,什么進(jìn)入warning.log,什么進(jìn)入info.log也需要決定。 ?
不同的文件嗎?好辦啊。就是不同的PrintWriter對(duì)象了。?
Java代碼??Logger?err_log?=?writer(err_writer);;?? Logger?warning_log?=?writer(warning_writer);;?? Logger?info_log?=?writer(info_writer);;??
各個(gè)文件記錄不同重要程度的信息是么??
Java代碼??err_log?=?filter(ERROR,?err_log,?nop(););;?? warning_log?=?filter(WARNING,?warning_log,?nop(););;?? info_log?=?filter(INFO,?info_log,?nop(););;??
最終把三個(gè)不同的logger串聯(lián)起來(lái)就是了:?
Java代碼??Logger?logger?=?sequence(err_log,?warning_log,?info_log);;??
3。也許可以象ant一樣把所有的信息都打印到一個(gè)文件中。 ?
這就更簡(jiǎn)單了,就一個(gè)是WriterLogger。?
4。每條logging信息是否要同時(shí)打印當(dāng)前的系統(tǒng)時(shí)間?也是一個(gè)需要抉擇的問(wèn)題。 ?
拿不定主意是么?沒(méi)關(guān)系,想好了再告訴我。?
反正,如果你需要系統(tǒng)時(shí)間,我只需要?
Java代碼??logger?=?timestamp(logger);;??
5。不僅僅是log文件,我們還希望能夠在標(biāo)準(zhǔn)錯(cuò)誤輸出上直接看見(jiàn)錯(cuò)誤,普通的信息可以打印到log文件中,對(duì)錯(cuò)誤信息,我們希望log文件和標(biāo)準(zhǔn)輸出上都有。 ?
可以創(chuàng)建針對(duì)標(biāo)準(zhǔn)輸出的Logger,然后和打印log 文件的logger串聯(lián)起來(lái)。?
Java代碼??Logger?std_logger?=?writer(new?PrintWriter(System.err););;?? std_logger?=?ignore(ERROR,?std_logger);;?? logger?=?sequence(std_logger,?logger);;??
6。標(biāo)準(zhǔn)輸出上的東西只要通知我們出錯(cuò)了就行,大概不需要詳細(xì)的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個(gè)簡(jiǎn)短的exception的message就夠了。 ?
這里需要對(duì)std_logger稍微改寫(xiě)一下:?
Java代碼??PrintWriter?out?=?new?PrintWriter(System.err);;?? std_logger?=?new?ErrorMessageLogger(out,?writer(out););;?? std_logger?=?ignore(ERROR,?std_logger,?nop(););;??
用ErrorMessageLogger來(lái)改寫(xiě)對(duì)異常的log邏輯。?
7。warning似乎也應(yīng)該輸出到屏幕上。 ?
好啊。就是把ignore函數(shù)里面的ERROR換成WARNING就好了?
Java代碼??std_logger?=?ignore(WARNING,?std_logger,?nop(););;??
8。不管文件里面是否要打印當(dāng)前系統(tǒng)時(shí)間,屏幕上應(yīng)該可以選擇不要打印時(shí)間。 ?
對(duì)std_logger不掉用timestamp就是了。?
9??蛻?hù)應(yīng)該可以通過(guò)命令行來(lái)決定log文件的名字。 ?
這條和logger組合子其實(shí)沒(méi)什么關(guān)系。?
10??蛻?hù)可以通過(guò)命令行來(lái)決定log的細(xì)節(jié)程度,比如,我們只關(guān)心info一樣級(jí)別的信息,至于debug, verbose的信息,對(duì)不起,不要打印。 ?
生成那個(gè)最終使用的Logger對(duì)象的時(shí)候,再ignore一下就行了:?
Java代碼??logger?=?ignore(some_level,?logger,?nop(););;??
11。neptune生成的是一些Command對(duì)象,這些對(duì)象運(yùn)行的時(shí)候如果出現(xiàn)exception,這些exception會(huì)帶有execution trace,這個(gè)execution trace可以告訴我們每個(gè)調(diào)用棧上的Command對(duì)象在原始的neptune文件中的位置(行號(hào))。?
這種exception叫做NeptuneException,它有一個(gè)printExecutionTrace(PrintWriter)的方法來(lái)打印execution trace。?
所以,對(duì)應(yīng)NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調(diào)用printExecutionTrace()。 ?
NeptuneExceptionLogger就是給這個(gè)準(zhǔn)備的呀。?
12。neptune使用的是jaskell語(yǔ)言,如果jaskell腳本運(yùn)行失敗,一個(gè)EvaluationException會(huì)被拋出,這個(gè)類(lèi)有一個(gè)printEvaluationTrace(PrintWriter)的方法來(lái)打印evaluation trace,這個(gè)trace用來(lái)告訴我們每個(gè)jaskell的表達(dá)式在腳本文件中的位置。?
所以,對(duì)應(yīng)EvaluationException,我們要在printStackTrace()之前,調(diào)用printEvaluationTrace()。 ?
JaskellExceptionLogger?
13。execution trace和evaluation trace應(yīng)該被打印到屏幕上和log文件兩個(gè)地方。 ?
這就是說(shuō),上面兩個(gè)Logger應(yīng)該被應(yīng)用到std_logger和logger兩個(gè)對(duì)象中。?
14。因?yàn)閜rintExecutionTrace()和printEvaluationTrace()本身已經(jīng)打印了這個(gè)異常的getMessage(),所以,對(duì)這兩種異常,我們就不再象對(duì)待其它種類(lèi)的異常那樣在屏幕上打印getMessage()了,以免重復(fù)。?
就是說(shuō),一旦一個(gè)exception被發(fā)現(xiàn)是NeptuneException,那么ErrorMessageLogger就要被跳過(guò)了。?
Java代碼??final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;?? final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;?? final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;?? return?neptune_logger;??
這個(gè)neptune_logger先判斷異常是不是NeptuneException,如果是,直接處理,否則,傳遞給jaskell_logger。jaskell_logger繼續(xù)判斷,如果不是它感興趣的,再傳遞給ErrorMessageLogger來(lái)做最后的缺省處理。?
15。也許還有一些暫時(shí)沒(méi)有想到的需求, 比如不是寫(xiě)入log文件,而是畫(huà)個(gè)圖之類(lèi)的變態(tài)要求 。?
放馬過(guò)來(lái)吧??次覀兊慕M合子能不能對(duì)付。?
[/list:u]?
很驚訝地發(fā)現(xiàn),就這么幾個(gè)小兒科似的積木,就似乎全部解決了曾讓我們煩惱的這些需求??
為了給大家一個(gè)完整的印象,下面是我實(shí)際項(xiàng)目中使用這些組合子應(yīng)對(duì)上面這些需求的代碼:?
Java代碼??public?class?StreamLogger?{?? ??private?final?OutputStream?out;?? ???? ??/**? ???*?To?create?a?StreamLogger?object.? ???*?@param?out?the?OutputStream?object?that?the?log?message?should?go?to.? ???*/?? ??public?StreamLogger(OutputStream?out);?{?? ????this.out?=?out;?? ??}?? ???? ??/**? ???*?To?get?the?OutputStream?that?the?log?messages?should?go?to.? ???*/?? ??public?OutputStream?getStream();?{?? ????return?out;?? ??}?? ??private?static?Logger?getBaseLogger(PrintWriter?writer);{?? ????final?Logger?nop?=?new?NopLogger();;?? ????final?Logger?base?=?Loggers.logger(writer);;?? ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?nop);;?? ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?nop);;?? ????return?Loggers.sequence(?? ????????new?Logger[]{neptune_logger,?jaskell_logger,?base}?? ????);;?? ??}?? ??private?static?Logger?getEchoLogger(PrintWriter?writer);{?? ????return?new?ErrorMessageLogger(writer);;?? ??}?? ??private?static?Logger?getErrorLogger(PrintWriter?writer);{?? ????final?Logger?err_logger?=?new?ErrorMessageLogger(writer);;?? ????final?Logger?jaskell_logger?=?new?JaskellExceptionLogger(writer,?err_logger);;?? ????final?Logger?neptune_logger?=?new?NeptuneExceptionLogger(writer,?jaskell_logger);;?? ????return?neptune_logger;?? ??}?? ??/**? ???*?Get?the?Logger?instance.? ???*?@param?min_level?the?minimal?critical?level?for?a?log?message?to?show?up?in?the?log.? ???*?@return?the?Logger?instance.? ???*/?? ??public?Logger?getDefaultLogger(int?min_level);{?? ????final?PrintWriter?writer?=?new?PrintWriter(out,?true);;?? ????final?PrintWriter?err?=?new?PrintWriter(System.err,?true);;?? ????final?PrintWriter?warn?=?new?PrintWriter(System.out,?true);;?? ????final?Logger?all?=?Loggers.sequence(new?Logger[]{?? ????????Loggers.ignore(getErrorLogger(err);,?Logger.ERROR);,?? ????????Loggers.filter(getEchoLogger(warn);,?Logger.WARNING);,?? ????????getBaseLogger(writer);?? ??????}?? ????);;?? ????return?Loggers.ignore(all,?min_level);;?? ??}?? }??
為了偷懶,我沒(méi)有用配置文件,就是把這些策略硬編碼進(jìn)java了。好在上面的代碼非常declarative,改起來(lái)也很容易。?
沒(méi)習(xí)慣讀代碼的朋友。這里奉勸還是讀一讀吧。很多時(shí)候,代碼才是說(shuō)明問(wèn)題的最好手段。我相信,只有讀了代碼,你才能真正嘗到CO的味道。?
有朋友問(wèn),你這個(gè)東西和decorator pattern有什么區(qū)別呀?乍看上去,還真是長(zhǎng)得差不多呢。不都是往現(xiàn)有的某個(gè)對(duì)象上面附加一些功能嗎??
也許是把。我不知道象SequenceLogger這種接受一個(gè)數(shù)組的,是否也叫做對(duì)數(shù)組的decorator;也不知道IgnoreLogger接受了兩個(gè)Logger對(duì)象,這個(gè)decorator究竟是修飾誰(shuí)的呢??
其實(shí),叫什么名字無(wú)所謂。我這里要講的,是一種從基本粒子推演組合的思路。形式上它也許碰巧象decorator, 象ioc。但是正如workinghard所說(shuō)(這句話(huà)深得我心),思路的切入點(diǎn)不同。?
如果你仔細(xì)看上面的代碼,也許你會(huì)有所感覺(jué):對(duì)Logger的千奇百怪的組合本身已經(jīng)有點(diǎn)象一個(gè)程序代碼了。?
如果用偽碼表示:?
Java代碼??all_logger?=?ignore?messages?below?ERROR?for?getErrorLogger(err);;?? ????????filter?messages?except?WARNING?for?getEchoLogger(warn);;?? ????????baseBaseLogger(writer);;?? ignore?messages?below?lvl?for?all_logger;??
當(dāng)組合子越來(lái)越多,需求越來(lái)越復(fù)雜,這個(gè)組合就會(huì)越來(lái)越象個(gè)程序。?
這里,實(shí)際上,(至少我希望如此),我們的思維已經(jīng)從打印什么什么東西上升為在Logger這個(gè)級(jí)別的組裝了。?
這也就是所謂higher order logic的意思。?
所謂combinator-oriented,在這里,就體現(xiàn)為系統(tǒng)性地在高階邏輯的層面上考慮問(wèn)題,而不是如decorator那樣的零敲碎打的幾個(gè)功能模塊。?
大量的需求邏輯被以聲明式的方式在高階邏輯中實(shí)現(xiàn),而基本的組合子只負(fù)責(zé)實(shí)現(xiàn)原字操作。?
當(dāng)然,缺點(diǎn)也是明顯的,對(duì)此我不諱言:?
[list]高階邏輯不容易調(diào)試,當(dāng)我們使用一個(gè)組合了十幾層的復(fù)雜的Logger對(duì)象的時(shí)候(真正用了co這種情況不少見(jiàn)),一旦出現(xiàn)bug,跟蹤的時(shí)候我們就會(huì)發(fā)現(xiàn)象是陷入了一個(gè)迷宮,從一個(gè)組合子跟蹤進(jìn)入另一個(gè)組合子,繞來(lái)繞去。?
另外,異常的stack trace也無(wú)法反映組合層次關(guān)系,造成錯(cuò)誤定位麻煩。[/list:u]?
這也許不是co本身的問(wèn)題,而是因?yàn)閖ava這種oo語(yǔ)言對(duì)co沒(méi)有提供語(yǔ)言上的支持。但是無(wú)論如何,這對(duì)在java中大規(guī)模使用co造成了障礙。?
也許你還無(wú)法理解。平時(shí)我們?cè)趈ava中用那么幾個(gè)decorator,本身非常簡(jiǎn)單,所以debug, trace都不是問(wèn)題。可是,一旦oriented起來(lái),情況就不同了。街上有兩輛車(chē)和成千上萬(wàn)輛車(chē),對(duì)交通造成的壓力截然不同。?
還有朋友,對(duì)co如何把握有疑問(wèn)。難道co就是瞎貓碰死耗子么??
其實(shí),無(wú)論co還是oo,對(duì)設(shè)計(jì)者都是有一定要求的。?
oo要求你了解需求,并且經(jīng)驗(yàn)豐富,也要有一點(diǎn)點(diǎn)運(yùn)氣。?
co也要求經(jīng)驗(yàn),這個(gè)經(jīng)驗(yàn)是設(shè)計(jì)組合子系統(tǒng)的經(jīng)驗(yàn)。什么樣的組合子是好的?怎么才算是足夠簡(jiǎn)單?什么組合規(guī)則是合理的?等等,這些,也有規(guī)律可循,就像oo的各種模式一樣。同時(shí),也可以refactor。畢竟,怎么簡(jiǎn)單地想問(wèn)題比怎么分解復(fù)雜問(wèn)題可能還是要容易掌握一點(diǎn)。?
不過(guò),co對(duì)經(jīng)驗(yàn)的要求稍微小一點(diǎn),但是對(duì)數(shù)學(xué)和邏輯的基本功要求則多了一點(diǎn)。有了一些數(shù)學(xué)和邏輯方面的基本功,那么設(shè)計(jì)組合子就會(huì)輕松的多。?
co也要有一點(diǎn)點(diǎn)運(yùn)氣。所以遇到你怎么想也想不明白的情況,就別死抗啦,也許這個(gè)問(wèn)題就抽象不出組合子來(lái),或者以我們普通人的智慧抽象不出來(lái)。?
co是銀彈嗎?當(dāng)然不是,至少以我的功力沒(méi)有這種自信。?
遇到復(fù)雜的問(wèn)題我也是先分解需求,面向接口的。只有問(wèn)題的規(guī)模被控制在了一定的范圍,我才會(huì)試圖用co來(lái)解決問(wèn)題??恐鴮?duì)co的一些經(jīng)驗(yàn)和感覺(jué),一旦發(fā)現(xiàn)了可以組合子化的概念,成果會(huì)非常顯著。?
而且,co和oo關(guān)注的層面也不同。co是針對(duì)一個(gè)單獨(dú)的概念(這點(diǎn)倒有點(diǎn)象ao),一點(diǎn)一點(diǎn)演繹,構(gòu)成一個(gè)可以任意復(fù)雜的系統(tǒng),一個(gè)好的co也會(huì)大大減少需要引入的概念數(shù)。而oo是處理大量互相或者有聯(lián)系,或者沒(méi)有聯(lián)系的概念,研究怎么樣把一個(gè)看上去復(fù)雜的系統(tǒng)的復(fù)雜度控制住。所以?xún)烧卟⒉皇腔ハ嗯懦獾?。自頂向?#xff0c;自底向上,也許還是兩手一起抓更好些。?
這段時(shí)間應(yīng)用co做了幾個(gè)軟件后,感覺(jué)co最擅長(zhǎng)的領(lǐng)域是:?
問(wèn)題域有比較少的概念,概念簡(jiǎn)明清晰(比如logger, predicate, factory,command),但是對(duì)概念的實(shí)現(xiàn)要求非常靈活的場(chǎng)合。?
這種時(shí)候,用oo就有無(wú)處下嘴之感。畢竟概念已經(jīng)分解了,職責(zé)也清楚,就是怎么提供一個(gè)可以靈活適應(yīng)變化的體系,而對(duì)這個(gè),oo并沒(méi)有提供一個(gè)方法論。基本上就是用po的方法吭哧吭哧硬做。而co此時(shí)就可以大展用武之地。彌補(bǔ)這個(gè)空隙。?
看過(guò)圣經(jīng)的,也許有感覺(jué),舊約里面的上帝嚴(yán)厲,經(jīng)典,就像是一個(gè)純粹的fp語(yǔ)言,每個(gè)程序員都被迫按照函數(shù)式,組合子的方式思考問(wèn)題,你感覺(jué)困難?那是你不夠虔誠(chéng)。你們?nèi)瞬缓衔业男囊?#xff0c;我淹死你們!?
而新約里面,明顯添加了很多人情味兒。上帝通過(guò)自己的兒子和人們和解了。既然淹死一波,再來(lái)一波還是這樣,那么是不是說(shuō)大家應(yīng)該各讓一步呢??
co和oo,既然各自都不能宣稱(chēng)自己就是銀彈,那么為什么不能拉起手來(lái)呢?我們不是神,不可能真正按照神的方式用基本粒子組合演化世界,所以就別象清教徒一樣苦苦追求不可能的目標(biāo)了。但是,上帝的組合之道畢竟相當(dāng)巧妙,在有些時(shí)候荊棘里面出現(xiàn)火焰的時(shí)候,我們又何必固執(zhí)地拒絕造物主的好意呢?禮貌地說(shuō)一聲:“謝了!老大”。不是皆大歡喜??
這一節(jié)我們繼續(xù)講解了這個(gè)logging的例子。實(shí)際上,logging是一個(gè)非常簡(jiǎn)單的組合子,下面如果大家對(duì)這個(gè)例子有疑問(wèn),我會(huì)盡力解答。?
我們不自量力,妄圖踩著上帝的步伐前進(jìn)。結(jié)果就是這么幾個(gè)簡(jiǎn)單的象白開(kāi)水似的類(lèi)。失望嗎?是不是造物試圖模仿造物主本身就是一種可笑的狂妄呢??
別急,讓我們失聲痛哭之前先看看我們這幾步走的是不是一錢(qián)不值。?
[list]?
1。logger可以把信息打印到log文件中。 ?
容易,直接創(chuàng)建一個(gè)WriterLogger就好了。?
2。不同的重要程度的信息也許可以打印到不同的文件中?象websphere,有error.log, info.log等。?
如果這樣,那么什么重要程度的信息進(jìn)入error.log,什么進(jìn)入warning.log,什么進(jìn)入info.log也需要決定。 ?
不同的文件嗎?好辦啊。就是不同的PrintWriter對(duì)象了。?
Java代碼??
各個(gè)文件記錄不同重要程度的信息是么??
Java代碼??
最終把三個(gè)不同的logger串聯(lián)起來(lái)就是了:?
Java代碼??
3。也許可以象ant一樣把所有的信息都打印到一個(gè)文件中。 ?
這就更簡(jiǎn)單了,就一個(gè)是WriterLogger。?
4。每條logging信息是否要同時(shí)打印當(dāng)前的系統(tǒng)時(shí)間?也是一個(gè)需要抉擇的問(wèn)題。 ?
拿不定主意是么?沒(méi)關(guān)系,想好了再告訴我。?
反正,如果你需要系統(tǒng)時(shí)間,我只需要?
Java代碼??
5。不僅僅是log文件,我們還希望能夠在標(biāo)準(zhǔn)錯(cuò)誤輸出上直接看見(jiàn)錯(cuò)誤,普通的信息可以打印到log文件中,對(duì)錯(cuò)誤信息,我們希望log文件和標(biāo)準(zhǔn)輸出上都有。 ?
可以創(chuàng)建針對(duì)標(biāo)準(zhǔn)輸出的Logger,然后和打印log 文件的logger串聯(lián)起來(lái)。?
Java代碼??
6。標(biāo)準(zhǔn)輸出上的東西只要通知我們出錯(cuò)了就行,大概不需要詳細(xì)的stack trace,所以exception stack trace可以打印到文件中,而屏幕上有個(gè)簡(jiǎn)短的exception的message就夠了。 ?
這里需要對(duì)std_logger稍微改寫(xiě)一下:?
Java代碼??
用ErrorMessageLogger來(lái)改寫(xiě)對(duì)異常的log邏輯。?
7。warning似乎也應(yīng)該輸出到屏幕上。 ?
好啊。就是把ignore函數(shù)里面的ERROR換成WARNING就好了?
Java代碼??
8。不管文件里面是否要打印當(dāng)前系統(tǒng)時(shí)間,屏幕上應(yīng)該可以選擇不要打印時(shí)間。 ?
對(duì)std_logger不掉用timestamp就是了。?
9??蛻?hù)應(yīng)該可以通過(guò)命令行來(lái)決定log文件的名字。 ?
這條和logger組合子其實(shí)沒(méi)什么關(guān)系。?
10??蛻?hù)可以通過(guò)命令行來(lái)決定log的細(xì)節(jié)程度,比如,我們只關(guān)心info一樣級(jí)別的信息,至于debug, verbose的信息,對(duì)不起,不要打印。 ?
生成那個(gè)最終使用的Logger對(duì)象的時(shí)候,再ignore一下就行了:?
Java代碼??
11。neptune生成的是一些Command對(duì)象,這些對(duì)象運(yùn)行的時(shí)候如果出現(xiàn)exception,這些exception會(huì)帶有execution trace,這個(gè)execution trace可以告訴我們每個(gè)調(diào)用棧上的Command對(duì)象在原始的neptune文件中的位置(行號(hào))。?
這種exception叫做NeptuneException,它有一個(gè)printExecutionTrace(PrintWriter)的方法來(lái)打印execution trace。?
所以,對(duì)應(yīng)NeptuneException,我們就不僅僅是printStackTrace()了,而是要在printStackTrace()之前調(diào)用printExecutionTrace()。 ?
NeptuneExceptionLogger就是給這個(gè)準(zhǔn)備的呀。?
12。neptune使用的是jaskell語(yǔ)言,如果jaskell腳本運(yùn)行失敗,一個(gè)EvaluationException會(huì)被拋出,這個(gè)類(lèi)有一個(gè)printEvaluationTrace(PrintWriter)的方法來(lái)打印evaluation trace,這個(gè)trace用來(lái)告訴我們每個(gè)jaskell的表達(dá)式在腳本文件中的位置。?
所以,對(duì)應(yīng)EvaluationException,我們要在printStackTrace()之前,調(diào)用printEvaluationTrace()。 ?
JaskellExceptionLogger?
13。execution trace和evaluation trace應(yīng)該被打印到屏幕上和log文件兩個(gè)地方。 ?
這就是說(shuō),上面兩個(gè)Logger應(yīng)該被應(yīng)用到std_logger和logger兩個(gè)對(duì)象中。?
14。因?yàn)閜rintExecutionTrace()和printEvaluationTrace()本身已經(jīng)打印了這個(gè)異常的getMessage(),所以,對(duì)這兩種異常,我們就不再象對(duì)待其它種類(lèi)的異常那樣在屏幕上打印getMessage()了,以免重復(fù)。?
就是說(shuō),一旦一個(gè)exception被發(fā)現(xiàn)是NeptuneException,那么ErrorMessageLogger就要被跳過(guò)了。?
Java代碼??
這個(gè)neptune_logger先判斷異常是不是NeptuneException,如果是,直接處理,否則,傳遞給jaskell_logger。jaskell_logger繼續(xù)判斷,如果不是它感興趣的,再傳遞給ErrorMessageLogger來(lái)做最后的缺省處理。?
15。也許還有一些暫時(shí)沒(méi)有想到的需求, 比如不是寫(xiě)入log文件,而是畫(huà)個(gè)圖之類(lèi)的變態(tài)要求 。?
放馬過(guò)來(lái)吧??次覀兊慕M合子能不能對(duì)付。?
[/list:u]?
很驚訝地發(fā)現(xiàn),就這么幾個(gè)小兒科似的積木,就似乎全部解決了曾讓我們煩惱的這些需求??
為了給大家一個(gè)完整的印象,下面是我實(shí)際項(xiàng)目中使用這些組合子應(yīng)對(duì)上面這些需求的代碼:?
Java代碼??
為了偷懶,我沒(méi)有用配置文件,就是把這些策略硬編碼進(jìn)java了。好在上面的代碼非常declarative,改起來(lái)也很容易。?
沒(méi)習(xí)慣讀代碼的朋友。這里奉勸還是讀一讀吧。很多時(shí)候,代碼才是說(shuō)明問(wèn)題的最好手段。我相信,只有讀了代碼,你才能真正嘗到CO的味道。?
有朋友問(wèn),你這個(gè)東西和decorator pattern有什么區(qū)別呀?乍看上去,還真是長(zhǎng)得差不多呢。不都是往現(xiàn)有的某個(gè)對(duì)象上面附加一些功能嗎??
也許是把。我不知道象SequenceLogger這種接受一個(gè)數(shù)組的,是否也叫做對(duì)數(shù)組的decorator;也不知道IgnoreLogger接受了兩個(gè)Logger對(duì)象,這個(gè)decorator究竟是修飾誰(shuí)的呢??
其實(shí),叫什么名字無(wú)所謂。我這里要講的,是一種從基本粒子推演組合的思路。形式上它也許碰巧象decorator, 象ioc。但是正如workinghard所說(shuō)(這句話(huà)深得我心),思路的切入點(diǎn)不同。?
如果你仔細(xì)看上面的代碼,也許你會(huì)有所感覺(jué):對(duì)Logger的千奇百怪的組合本身已經(jīng)有點(diǎn)象一個(gè)程序代碼了。?
如果用偽碼表示:?
Java代碼??
當(dāng)組合子越來(lái)越多,需求越來(lái)越復(fù)雜,這個(gè)組合就會(huì)越來(lái)越象個(gè)程序。?
這里,實(shí)際上,(至少我希望如此),我們的思維已經(jīng)從打印什么什么東西上升為在Logger這個(gè)級(jí)別的組裝了。?
這也就是所謂higher order logic的意思。?
所謂combinator-oriented,在這里,就體現(xiàn)為系統(tǒng)性地在高階邏輯的層面上考慮問(wèn)題,而不是如decorator那樣的零敲碎打的幾個(gè)功能模塊。?
大量的需求邏輯被以聲明式的方式在高階邏輯中實(shí)現(xiàn),而基本的組合子只負(fù)責(zé)實(shí)現(xiàn)原字操作。?
當(dāng)然,缺點(diǎn)也是明顯的,對(duì)此我不諱言:?
[list]高階邏輯不容易調(diào)試,當(dāng)我們使用一個(gè)組合了十幾層的復(fù)雜的Logger對(duì)象的時(shí)候(真正用了co這種情況不少見(jiàn)),一旦出現(xiàn)bug,跟蹤的時(shí)候我們就會(huì)發(fā)現(xiàn)象是陷入了一個(gè)迷宮,從一個(gè)組合子跟蹤進(jìn)入另一個(gè)組合子,繞來(lái)繞去。?
另外,異常的stack trace也無(wú)法反映組合層次關(guān)系,造成錯(cuò)誤定位麻煩。[/list:u]?
這也許不是co本身的問(wèn)題,而是因?yàn)閖ava這種oo語(yǔ)言對(duì)co沒(méi)有提供語(yǔ)言上的支持。但是無(wú)論如何,這對(duì)在java中大規(guī)模使用co造成了障礙。?
也許你還無(wú)法理解。平時(shí)我們?cè)趈ava中用那么幾個(gè)decorator,本身非常簡(jiǎn)單,所以debug, trace都不是問(wèn)題。可是,一旦oriented起來(lái),情況就不同了。街上有兩輛車(chē)和成千上萬(wàn)輛車(chē),對(duì)交通造成的壓力截然不同。?
還有朋友,對(duì)co如何把握有疑問(wèn)。難道co就是瞎貓碰死耗子么??
其實(shí),無(wú)論co還是oo,對(duì)設(shè)計(jì)者都是有一定要求的。?
oo要求你了解需求,并且經(jīng)驗(yàn)豐富,也要有一點(diǎn)點(diǎn)運(yùn)氣。?
co也要求經(jīng)驗(yàn),這個(gè)經(jīng)驗(yàn)是設(shè)計(jì)組合子系統(tǒng)的經(jīng)驗(yàn)。什么樣的組合子是好的?怎么才算是足夠簡(jiǎn)單?什么組合規(guī)則是合理的?等等,這些,也有規(guī)律可循,就像oo的各種模式一樣。同時(shí),也可以refactor。畢竟,怎么簡(jiǎn)單地想問(wèn)題比怎么分解復(fù)雜問(wèn)題可能還是要容易掌握一點(diǎn)。?
不過(guò),co對(duì)經(jīng)驗(yàn)的要求稍微小一點(diǎn),但是對(duì)數(shù)學(xué)和邏輯的基本功要求則多了一點(diǎn)。有了一些數(shù)學(xué)和邏輯方面的基本功,那么設(shè)計(jì)組合子就會(huì)輕松的多。?
co也要有一點(diǎn)點(diǎn)運(yùn)氣。所以遇到你怎么想也想不明白的情況,就別死抗啦,也許這個(gè)問(wèn)題就抽象不出組合子來(lái),或者以我們普通人的智慧抽象不出來(lái)。?
co是銀彈嗎?當(dāng)然不是,至少以我的功力沒(méi)有這種自信。?
遇到復(fù)雜的問(wèn)題我也是先分解需求,面向接口的。只有問(wèn)題的規(guī)模被控制在了一定的范圍,我才會(huì)試圖用co來(lái)解決問(wèn)題??恐鴮?duì)co的一些經(jīng)驗(yàn)和感覺(jué),一旦發(fā)現(xiàn)了可以組合子化的概念,成果會(huì)非常顯著。?
而且,co和oo關(guān)注的層面也不同。co是針對(duì)一個(gè)單獨(dú)的概念(這點(diǎn)倒有點(diǎn)象ao),一點(diǎn)一點(diǎn)演繹,構(gòu)成一個(gè)可以任意復(fù)雜的系統(tǒng),一個(gè)好的co也會(huì)大大減少需要引入的概念數(shù)。而oo是處理大量互相或者有聯(lián)系,或者沒(méi)有聯(lián)系的概念,研究怎么樣把一個(gè)看上去復(fù)雜的系統(tǒng)的復(fù)雜度控制住。所以?xún)烧卟⒉皇腔ハ嗯懦獾?。自頂向?#xff0c;自底向上,也許還是兩手一起抓更好些。?
這段時(shí)間應(yīng)用co做了幾個(gè)軟件后,感覺(jué)co最擅長(zhǎng)的領(lǐng)域是:?
問(wèn)題域有比較少的概念,概念簡(jiǎn)明清晰(比如logger, predicate, factory,command),但是對(duì)概念的實(shí)現(xiàn)要求非常靈活的場(chǎng)合。?
這種時(shí)候,用oo就有無(wú)處下嘴之感。畢竟概念已經(jīng)分解了,職責(zé)也清楚,就是怎么提供一個(gè)可以靈活適應(yīng)變化的體系,而對(duì)這個(gè),oo并沒(méi)有提供一個(gè)方法論。基本上就是用po的方法吭哧吭哧硬做。而co此時(shí)就可以大展用武之地。彌補(bǔ)這個(gè)空隙。?
看過(guò)圣經(jīng)的,也許有感覺(jué),舊約里面的上帝嚴(yán)厲,經(jīng)典,就像是一個(gè)純粹的fp語(yǔ)言,每個(gè)程序員都被迫按照函數(shù)式,組合子的方式思考問(wèn)題,你感覺(jué)困難?那是你不夠虔誠(chéng)。你們?nèi)瞬缓衔业男囊?#xff0c;我淹死你們!?
而新約里面,明顯添加了很多人情味兒。上帝通過(guò)自己的兒子和人們和解了。既然淹死一波,再來(lái)一波還是這樣,那么是不是說(shuō)大家應(yīng)該各讓一步呢??
co和oo,既然各自都不能宣稱(chēng)自己就是銀彈,那么為什么不能拉起手來(lái)呢?我們不是神,不可能真正按照神的方式用基本粒子組合演化世界,所以就別象清教徒一樣苦苦追求不可能的目標(biāo)了。但是,上帝的組合之道畢竟相當(dāng)巧妙,在有些時(shí)候荊棘里面出現(xiàn)火焰的時(shí)候,我們又何必固執(zhí)地拒絕造物主的好意呢?禮貌地說(shuō)一聲:“謝了!老大”。不是皆大歡喜??
這一節(jié)我們繼續(xù)講解了這個(gè)logging的例子。實(shí)際上,logging是一個(gè)非常簡(jiǎn)單的組合子,下面如果大家對(duì)這個(gè)例子有疑問(wèn),我會(huì)盡力解答。?
然后,我們會(huì)進(jìn)軍下一個(gè)例子。
from:http://ajoo.iteye.com/blog/23307
總結(jié)
以上是生活随笔為你收集整理的面向组合子程序设计方法 之 新约的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 论面向组合子程序设计方法 之 燃烧的荆棘
- 下一篇: 论面向组合子程序设计方法 之 oracl