|
一個(gè)人自?shī)首詷?lè)的寫(xiě)個(gè)小程序,跟一幫人一起寫(xiě)個(gè)大程序。真的是不一樣。
自己一個(gè)人,根本就不存在交流,相互理解的問(wèn)題。人越多,理解他人意圖,向他人解釋意圖就越來(lái)越花時(shí)間。只要是需要交流的任務(wù),并非是人越多越好。有人加入,為了使加入的人有事做,原來(lái)的事就要重新劃分,而分開(kāi)之后要配合,又要花時(shí)間交流。發(fā)覺(jué)很多重要的軟件開(kāi)始都是幾個(gè)人做出來(lái)的。而漫畫(huà)中,進(jìn)行任務(wù)也采用小組模式,好像<幽游>, <獵人>都是四人小組。
這里,最基本的問(wèn)題就是任務(wù)的劃分,最理想的劃分是相互獨(dú)立。而要做到這種獨(dú)立,正交的劃分,是很困難的,更困難的是你會(huì)發(fā)現(xiàn)那最初的任務(wù)會(huì)隨著時(shí)間變動(dòng)。傳統(tǒng)的軟件工程會(huì)說(shuō),首先是定義需求,跟著大體設(shè)計(jì),詳細(xì)設(shè)計(jì),編碼,單元測(cè)試,整體測(cè)試等等。但真正實(shí)施起來(lái),發(fā)覺(jué)沒(méi)有那樣理想,很多項(xiàng)目都不是重新編寫(xiě),而是在原有的代碼上加強(qiáng),很可能原來(lái)的根本就是沙地,很是流沙,而卻要在上面起高樓。假如一個(gè)項(xiàng)目規(guī)定是半年完成,花上一個(gè)月去定義需求和設(shè)計(jì),跟著去編碼了,就會(huì)發(fā)現(xiàn)很多事情是想不到的,有些看來(lái)很簡(jiǎn)單的事一卡就卡上一個(gè)月,毫無(wú)進(jìn)展。發(fā)覺(jué)這本來(lái)是很簡(jiǎn)單的問(wèn)題牽涉到項(xiàng)目的結(jié)構(gòu),而之前結(jié)構(gòu)很難修改,又或者不舍得去修改,為解決這問(wèn)題,就使出一些歪招,看起來(lái)好像很巧妙,卻打亂了原來(lái)的結(jié)構(gòu),跟著這些古古怪怪,想不到的問(wèn)題一個(gè)個(gè)冒出,原來(lái)的設(shè)計(jì)漸漸偏離,又沒(méi)有去修改文檔。時(shí)間越來(lái)越緊,項(xiàng)目拖后,公司上層發(fā)覺(jué)不對(duì)路了,就加人,新人來(lái),拿起最初的設(shè)計(jì)文檔看,發(fā)覺(jué)根本對(duì)不上。舊人就又花時(shí)間會(huì)幫他理解項(xiàng)目。到原定的發(fā)布限期,程序卻一運(yùn)行就死機(jī),最終結(jié)果遲上兩三個(gè)月發(fā)布,發(fā)布之后很多bug, 再花兩三個(gè)月改錯(cuò),發(fā)個(gè)補(bǔ)丁包。一年就此過(guò)去。
軟件中常說(shuō)的任務(wù)劃分,其實(shí)就是要確立邊界條件。比如劃分出組件,組件中劃分出類,組件與組件,類和類交互都要通過(guò)一些接口。邊界條件最容易出問(wèn)題。本來(lái)我這個(gè)類好好的,一和另一個(gè)類交互,就出問(wèn)題了,又比如這個(gè)函數(shù)好好的,一到線程切換那一瞬間就出問(wèn)題。接口的定義很重要。軟件還沒(méi)有完全做到硬件那樣即插即用。先確立接口,跟著找不同的人做實(shí)現(xiàn),最后嵌起來(lái)就可以用,這也是很理想的一種情況。很可能最初那樣劃分接口就錯(cuò)了,另一種可能是實(shí)現(xiàn)者理解錯(cuò)了接口的含義。很多事,做完了才知道是做錯(cuò)了。比如考試,通常考完出來(lái)就知道答案,想著下次好好準(zhǔn)備,考好點(diǎn),卻發(fā)覺(jué)下次還是有題目是不會(huì)做。
我現(xiàn)在自己的意見(jiàn)(以后可能也會(huì)變), 項(xiàng)目初期是無(wú)論如何都想不出具體的細(xì)節(jié)的,所以只要有個(gè)大方向就可以了, 要盡快動(dòng)手做。既然之前沒(méi)有想出所有細(xì)節(jié),就強(qiáng)調(diào)要可以很容易的調(diào)整代碼的結(jié)構(gòu),添加更多的細(xì)節(jié),也就是重構(gòu)。注意,重構(gòu)并非是添加新功能,而是在不改變程序表現(xiàn)的情況下整理代碼,使原來(lái)的代碼更合理,之后再添新功能就容易了。而所謂的添個(gè)if來(lái)判斷空指針,再對(duì)話框上面加個(gè)按鈕等等,就不算是重構(gòu)了。
為修改代碼更容易,應(yīng)該注意某些小細(xì)節(jié)。特別是工程比較大的時(shí)候。有些習(xí)慣要一開(kāi)始就養(yǎng)成,不要想著只是做些小玩意玩玩,沒(méi)有關(guān)系啦。一開(kāi)始就應(yīng)向著這行業(yè)最top的那批人看齊,這樣才有可能做到專業(yè)。
1. 減少編譯連接時(shí)間,特別盡量不要在頭文件上加頭文件。
以前也很隨意的亂加頭文件,反正小工程,一下就編譯完了。現(xiàn)在發(fā)覺(jué)只要我改某個(gè)頭文件,等它編譯完夠看一章書(shū)了,經(jīng)過(guò)編譯折磨,才發(fā)覺(jué)這很重要。因?yàn)轭^文件一修改,包含或者間接包含的cpp文件就要被編譯。亂包含,會(huì)發(fā)現(xiàn)底層的文件一改,幾乎整個(gè)工程都編譯,是很費(fèi)時(shí)間的。另外就不要將所有類定義都寫(xiě)在一個(gè)地方,這樣包含依賴會(huì)減少一些。
如果編譯時(shí)間很長(zhǎng),明明知道有些地方不妥,也不會(huì)去修改的。好像寫(xiě)程序的都很怕麻煩。
2. 變量用到才定義,不要一開(kāi)始就定義。
很多人還保留C的習(xí)慣,將所有用到的變量都定義在函數(shù)開(kāi)頭。這習(xí)慣其實(shí)很不好,一方面沒(méi)必要的構(gòu)造析構(gòu)會(huì)被調(diào)用。更重要的是,你會(huì)發(fā)覺(jué)以后想將原來(lái)的函數(shù)分解成一些小函數(shù)時(shí)候,會(huì)很麻煩,因?yàn)槎x在開(kāi)頭,作用域是整個(gè)函數(shù)的,你想提出一段代碼,很難確定那些變量要用到,那些不用。另外變量定義一定要附上初始值,這個(gè)錯(cuò)誤看起來(lái)很弱智,但很多人會(huì)犯,特別是喜歡將所有變量就放在開(kāi)頭的那種C風(fēng)格的人。
3. 用類管理資源,獲取資源跟釋放資源盡量在同一個(gè)地方,不要分開(kāi)在兩處。特別是不要在同一個(gè)函數(shù)中不要new, delete同一對(duì)象或者數(shù)組。
資源是很廣義的,比如取gdi對(duì)象和釋放,常說(shuō)的是內(nèi)存。要復(fù)制一個(gè)字串,或要暫時(shí)讀一內(nèi)存,開(kāi)始又不知道長(zhǎng)度,很多人會(huì)new 一個(gè)char數(shù)組,跟著函數(shù)末尾在釋放。這樣就有問(wèn)題,比如中間有一個(gè)return, 跳過(guò)了釋放的語(yǔ)句,就有資源泄露。對(duì)此很多人堅(jiān)持一個(gè)函數(shù)一個(gè)出口,但是這樣往往有一些變量標(biāo)記性著是否結(jié)束循環(huán)等等,遠(yuǎn)不如一個(gè)return直接。另外換另一個(gè)人來(lái)修改代碼,他很可能不信奉一函數(shù)一出口。隨著函數(shù)修改變長(zhǎng),對(duì)應(yīng)的獲取釋放相隔越來(lái)越遠(yuǎn)。怕麻煩,為了不在每個(gè)return之前添上釋放代碼,有人就用goto out: 替代return, out:之后做釋放。
只要出現(xiàn)goto, 資源跟釋放隔太遠(yuǎn),以后想將一些代碼提出來(lái),做成另一個(gè)函數(shù),就很麻煩。
所以C++有個(gè)慣用法RAII, 簡(jiǎn)單理念是用類來(lái)管理資源,在構(gòu)造函數(shù)中獲取,在析構(gòu)釋放。因?yàn)闃?biāo)準(zhǔn)保證每個(gè)出口,已生成的對(duì)象其析構(gòu)會(huì)被調(diào)用,包括發(fā)生異常。如果要分配char數(shù)組,可以用std::vector mem; mem.resize(memSize)來(lái)替代, new char[memSize];, 之后的指針可以寫(xiě)成&mem[0]。 有人可能會(huì)說(shuō),如果對(duì)象太大,可能直接定義會(huì)引起棧溢出,所以要new. 如果真的是這樣,很可能是那個(gè)對(duì)象的設(shè)計(jì)本身就有問(wèn)題。要是老是寫(xiě)這樣一些釋放的輔助類很煩人,看看Loki::ScopeGuaid.
4. 風(fēng)格保持一致
風(fēng)格看起來(lái)是很個(gè)人,很細(xì)微的事情,但對(duì)于一幫人保持協(xié)調(diào)也是很重要,最忌的是團(tuán)隊(duì)每個(gè)人,或者個(gè)人在不同時(shí)候的風(fēng)格都不一樣。因?yàn)轱L(fēng)格不同,在從一種風(fēng)格逃到另一種風(fēng)格時(shí),思維上會(huì)卡一卡。另外接口命名的風(fēng)格不一樣,就很容易的將接口用錯(cuò)。
比如,有個(gè)迭代器類,要判斷前進(jìn)是否合理,有千奇百怪的名字,hasMove, hasMoveElements, isCurValie, isValie, isOk isLegal。又或者判斷有幾個(gè)元素,有名字getLength, length, size, numElemnts, totalElements。你用那些類,大概會(huì)罵最初寫(xiě)代碼的那批人太白癡。但想想,有幾個(gè)人是會(huì)將風(fēng)格保持一致的。
其實(shí)風(fēng)格本身沒(méi)有好壞,最怕的是不統(tǒng)一,選擇一種,跟著用就是了。但現(xiàn)實(shí)種往往是一批人同做一項(xiàng)目,各人都有自己寫(xiě)法,還鄙視他人的寫(xiě)法。假如A是取名成stl那風(fēng)格,小寫(xiě)加下劃線,B取命成MFC那種,C取名成Java那種,看起來(lái)會(huì)很累。這個(gè)問(wèn)題是很普遍的,寫(xiě)代碼的很多人都很聰明,也很自傲,往往覺(jué)得自己做的才是最好的。
另一點(diǎn)是,相同功能類的那些函數(shù)接口,如果有同樣的函數(shù),應(yīng)該將名字取成一致,不要搞得很亂。
一幫人一起會(huì)做一件事,會(huì)確立規(guī)矩,比如一起會(huì)玩游戲啊,出去玩啊,會(huì)商議定個(gè)時(shí)間。有類人當(dāng)別人商量規(guī)矩時(shí)候,他不出聲,或者是做別的事,比如玩手機(jī)發(fā)短信之類。規(guī)矩定好了,自己不清楚,就老是問(wèn),或者犯規(guī)了就怪規(guī)矩定得不合理。這類人是很影響士氣的,觀察一下,你身邊應(yīng)該會(huì)有類似的人。
5. 最后一條,老原則,Keep it simple and stupid.
程序首先是給人看的,之后才是被計(jì)算機(jī)看的。一定要簡(jiǎn)單。比如接口設(shè)計(jì)開(kāi)始要最簡(jiǎn)化,不要想著這接口以后會(huì)用到就加上,要想著以后可能沒(méi)有用就去掉。如果你自我陶醉,覺(jué)得自己很聰明,這樣巧妙的代碼也可以寫(xiě)得出,將來(lái)還有什么軟件寫(xiě)不出來(lái),注意,這樣想會(huì)有問(wèn)題。因?yàn)樵绞乔擅畹拇a,以后修改的人會(huì)越難理解,越容易出錯(cuò)。其實(shí)簡(jiǎn)單的代碼才難寫(xiě)。最好的設(shè)計(jì)應(yīng)是理所當(dāng)然,順理成章的,感覺(jué)不到有多巧妙。<孫子>中有句話,善戰(zhàn)者,無(wú)功名,無(wú)勇功。因?yàn)樗麄內(nèi)ゴ蛘蹋蛑熬挖A定了,根本就不需要很勇敢,很激烈才能打贏,一切順理成章,好像沒(méi)有什么難度,自然不會(huì)被人去歌頌。同樣,需要很巧妙的方法才能解決某問(wèn)題,本身就落下乘。
it知識(shí)庫(kù):關(guān)于程序的一些看法和簡(jiǎn)單建議,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。