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