做了4、5年的開(kāi)發(fā),大大小小的項(xiàng)目也做了不少,但以前都有一個(gè)致命的問(wèn)題,不知不覺(jué)就會(huì)寫(xiě)出一個(gè)巨大的主程序出來(lái),層次復(fù)雜,編碼痛苦,調(diào)試?yán)щy。但似乎大家都認(rèn)同這樣的開(kāi)發(fā)方式,雖然都知道界面和功能分離是好事情,但就是做不到。我自己也曾痛苦的思考過(guò),但沒(méi)有什么收效,似乎在Windows下的開(kāi)發(fā)只能是這么痛苦。
一星期前買了<<unix編程藝術(shù)>>,這一周可謂改天換地,每天都在閱讀和思考中度過(guò),想必武俠小說(shuō)中的武功大進(jìn)也就是這個(gè)意思了。雖然書(shū)還沒(méi)看完,但是有些話實(shí)在是不吐不快。
什么是界面?界面就是功能的子集。沒(méi)有哪個(gè)界面能反映所有的功能,但是若沒(méi)有界面,對(duì)于最終用戶來(lái)說(shuō)又是不可忍受的,無(wú)論如何都不能指望讓一個(gè)門衛(wèi)學(xué)會(huì)輸入復(fù)雜的命令來(lái)完成工作,雖然最終用戶也包括專業(yè)人士,但這世界上終究普通人更多。在這樣的前提下,可以認(rèn)為功能永遠(yuǎn)比界面更寬泛,更有適應(yīng)性,而GUI更狹窄,更具有特殊性,所以將界面和功能進(jìn)行分拆也就成為一種必然趨勢(shì)。
但是如何分拆?在Windows的世界里,一個(gè)普遍觀點(diǎn)就是DLL。DLL很好,但是還不夠好,因?yàn)闊o(wú)法直接使用、調(diào)試以及升級(jí),帶來(lái)的問(wèn)題遠(yuǎn)比好處多。另一種方法就是在代碼級(jí)進(jìn)行分層,比如GUI一層,功能一層,再用膠合層將二者整合。且不論膠合層的不可復(fù)用和調(diào)試?yán)щy,就一條,如何能做到GUI崩潰的時(shí)候卻不影響功能的實(shí)現(xiàn)?以前我做過(guò)的項(xiàng)目都是這樣處理的,直接的后果就是項(xiàng)目越到后期問(wèn)題越多,代碼越不接受變化。調(diào)試花費(fèi)了大量的人力物力,收效卻未必好,功能的一點(diǎn)點(diǎn)小修改就會(huì)造成代碼里出現(xiàn)意大利面條。你可以說(shuō)只要前期的小心規(guī)劃和仔細(xì)架構(gòu)就能避免這些問(wèn)題,但是誰(shuí)能準(zhǔn)確預(yù)測(cè)未來(lái)?無(wú)論做怎樣的努力,你也不能保證現(xiàn)在的功能永遠(yuǎn)不變,永遠(yuǎn)不變的恰恰就是變。如果不能保持實(shí)現(xiàn)的穩(wěn)定性和較好的移植性,這樣的項(xiàng)目下場(chǎng)一般都不太好。
說(shuō)了這么多廢話,還是趕緊進(jìn)入正題。談?wù)勥@一周來(lái)的心得體會(huì)!
首先,變化是漸進(jìn)的,非突變式的。如果能將變化的所在約束在一個(gè)比較小的代碼范圍內(nèi),修改就不會(huì)成為噩夢(mèng)。怎么約束?就一個(gè)要求:在保證完整性的條件下讓每個(gè)模塊包含的功能盡量單一和足夠小。首先是保證完整性,不是代碼足夠短,包含的實(shí)現(xiàn)足夠少就是完整,要達(dá)到完整,就要讓模塊的各個(gè)部分做到不可分割和無(wú)需添加,按照古人的說(shuō)法,就是增一分則太多,減一分則太少。這個(gè)要求雖然看起來(lái)很好理解,其實(shí)并沒(méi)有什么標(biāo)準(zhǔn)答案,每個(gè)人心里都有自己的回答,正所謂仁者見(jiàn)仁,智者見(jiàn)智。其次是單一化和小型化,一般來(lái)說(shuō),范圍太大的東西會(huì)造成人腦覆蓋不全,比如一個(gè)功能,如果牽扯的部分過(guò)多,就會(huì)造成從底層到中間層,再到上層,全部都要思考到,估計(jì)沒(méi)有幾個(gè)人能做到,即使做到了,將來(lái)的維護(hù)和修改也會(huì)變成噩夢(mèng)。相反,只要功能的涉及面夠窄,就很容易進(jìn)行思考和修改,這個(gè)道理應(yīng)該沒(méi)有什么問(wèn)題。
既然要保證模塊單一、小型化和保證完整,也就意味著這個(gè)模塊可以認(rèn)為是一個(gè)完整而單獨(dú)的程序,無(wú)需外圍程序的支持就可以單獨(dú)運(yùn)行和測(cè)試。從而引出我的最重要的觀點(diǎn):盡量用多進(jìn)程來(lái)分拆程序。在Windows的世界里,多進(jìn)程似乎是天生被忽略和鄙視的,從unix的觀點(diǎn)看,其主要原因是Windows的設(shè)計(jì)中對(duì)進(jìn)程的快速創(chuàng)建支持不夠,造成對(duì)多進(jìn)程的天然排斥和害怕。但是換一個(gè)思路看,多進(jìn)程也許是目前最好的架構(gòu)方式。底層的功能分拆成各個(gè)進(jìn)程單獨(dú)運(yùn)行,通過(guò)ipc和上層的GUI進(jìn)行交互,膠合層薄了,移植性增強(qiáng)了,調(diào)試容易了,功能演進(jìn)也不再成為噩夢(mèng)。需求永遠(yuǎn)是漸變的,所以進(jìn)程的漸變也就成為可控的行為。
? 多進(jìn)程間的傳遞方式一般有這么幾種:共享內(nèi)存,管道(pipe),信號(hào),消息,
socket。其中共享內(nèi)存適宜于大量數(shù)據(jù)的即時(shí)傳遞,速度快,容量大。但使用共享內(nèi)存時(shí)需要仔細(xì)考慮讀寫(xiě)沖突問(wèn)題,一般的解決辦法是用全局鎖,但是鎖的存在必然會(huì)造成效率的下降,所以能不用鎖就盡量不要用。pipe的速度和容量都沒(méi)有共享內(nèi)存好,但是用來(lái)傳遞命令和返回值還是很適合的。信號(hào)和消息的方式一般會(huì)和操作系統(tǒng)聯(lián)系緊密,個(gè)人不太喜歡。最后是socket,對(duì)于異地交互而言,socket是目前很常用的手段,甚至本地進(jìn)程間通訊也可以使用。但是由于和網(wǎng)絡(luò)有關(guān),所以同步性不好保證,需要辯證的使用。
說(shuō)了這么多,舉個(gè)例子說(shuō)明一下。假設(shè)現(xiàn)在要做一套點(diǎn)菜軟件供酒店使用,其基本功能包括人員管理,桌臺(tái)管理,點(diǎn)菜管理,結(jié)賬以及后臺(tái)管理五個(gè)功能模塊。按照單進(jìn)程的方式就是將所有功能整合在一起,系統(tǒng)啟動(dòng)時(shí)加載所有的功能,一旦某個(gè)模塊出現(xiàn)問(wèn)題,則必須重新啟動(dòng)程序,而且各個(gè)模塊之間很容易發(fā)生資源沖突和請(qǐng)求沖突。如果換成多進(jìn)程方式,讓我們看看有什么不同。首先是所有的功能最終目的地都是數(shù)據(jù)庫(kù),那么可以開(kāi)發(fā)一個(gè)后臺(tái)進(jìn)程專門所有負(fù)責(zé)針對(duì)數(shù)據(jù)庫(kù)的請(qǐng)求,通過(guò)pipe或者共享內(nèi)存來(lái)接收命令和返回結(jié)果,那么程序或者說(shuō)具體代碼塊之間的接口就是單一的pipe或共享內(nèi)存了。同時(shí),即使某一個(gè)程序運(yùn)行錯(cuò)誤也不會(huì)造成整體失敗,只需要重起失敗的部分即可。當(dāng)然了,這種方式下存在一個(gè)問(wèn)題,就是效率的降低,但是對(duì)于大多數(shù)的應(yīng)用來(lái)說(shuō),穩(wěn)定性的提高遠(yuǎn)比效率的降低要重要,而且隨著硬件水平的不斷提高,效率總是可以達(dá)標(biāo)的。
數(shù)據(jù)庫(kù)處理分拆出去后,剩下的就很好處理了,人員、點(diǎn)菜、桌臺(tái)等管理模塊都作為單獨(dú)的后臺(tái)程序出現(xiàn),最后GUI部分只需要和各個(gè)共享內(nèi)存和pipe打交道即可,無(wú)需只要具體的邏輯處理和功能實(shí)現(xiàn),而且各個(gè)后臺(tái)程序還可以復(fù)用,比如人員管理可以挪到客房服務(wù)系統(tǒng)中,甚至是其他系統(tǒng)。GUI隨時(shí)可以替換,實(shí)現(xiàn)了功能和界面的分離,同時(shí)系統(tǒng)崩潰的幾率大大降低,升級(jí)和售后也方便很多,永遠(yuǎn)不要把最終用戶想的太愚蠢,很多時(shí)候人們還是蠻有求知欲的。
更多的細(xì)節(jié)需要自己整理,這里只是給出了一個(gè)框架,起碼我現(xiàn)在的項(xiàng)目已經(jīng)開(kāi)始這樣做,效果嘛,半年后就知道了。?
2006-04-05 21:38?
修改于2006-04-07 18:56
再次修改于2006-04-13 21:55