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