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