每一個人對於什麼是「好」的代碼規範未必認同,這時咱們頗有必要給出一個基準線—什麼是好的代碼規範和設計規範java
計算機只關心編譯生成的機器碼,你的程序採用哪一種縮進風格,變量名有無統一的規範等,與機器碼的執行無關。可是,作一個有商業價值的項目,或者在團隊裏工做,代碼規範至關重要。「代碼規範」能夠分紅兩個部分:mysql
1. 代碼風格規範——主要是文字上的規定,看似表面文章,實際上很是重要程序員
2. 代碼設計規範——牽涉到程序設計、模塊之間的關係、設計模式等方方面面的通用原則算法
代碼風格的原則是:簡明,易讀,無二義性
提示:這裏談的風格是一家之言,如遇爭執,關鍵是要本着「保持簡明,讓代碼更容易讀」的原則,看看爭執中的代碼規範可否讓程序員們更好地理解和維護程序sql
是用Tab鍵好,仍是二、四、8個空格?
結論:4個空格,在Visual Studio和其餘的一些編輯工具中均可以定義Tab鍵擴展成爲幾個空格鍵。不用Tab鍵的理由是,Tab鍵在不一樣的狀況下會顯示不一樣的長度,嚴重干擾閱讀體驗。4個空格的距離從可讀性來講,正好數據庫
行寬必須限制,可是之前有些文檔規定的80字符行寬過小了(之前的計算機/打字機顯示行寬爲80字符),如今時代不一樣了,能夠限定爲100字符編程
在複雜的條件表達式中,用括號清楚地表示邏輯優先級設計模式
1. 最精簡的格式A:網絡
if (condition) DoSomething(); else DoSomethingElse();
優勢:由於能夠節省幾行
缺點:不一樣的語句(Statement)放在一行中,程序調試(Debug)起來很是不方便,若是要一步一步觀察condition中各個變量(condition多是包含函數調用的複雜表達式)的變化狀況,單步執行就很難了數據結構
2. 有斷行的格式B:
if (condition) DoSomething(); else DoSomethingElse();
缺點:因爲沒有明確的「{」和「}」來判斷程序的結構,在有多層控制嵌套時,這樣的格式就不容易看清結構和對應關係
3. 改進的格式C
if (condition) { DoSomething(); } else { DoSomethingElse(); }
缺點:不夠清晰
4. 每一個「{」和「}」都獨佔一行,即格式D
if (condition) { DoSomething(); } else { DoSomethingElse(); }
不要把多條語句放在一行上
a =1; b =2; // bogus if (fFoo) Bar(); // bogus
更嚴格地說,不要把多個變量定義在一行上
Foo foo1, foo2; // bogus
用單個字母給有複雜語義的實體命名並不可取,也是通過了實踐檢驗的方法叫「匈牙利命名法」。例如:
fFileExist // 代表是一個bool值,表示文件是否存在; szPath // 代表是一個以0結束的字符串,表示一個路徑
下劃線用來分隔變量名字中的做用域標註和變量的語義,如:一個類型的成員變量一般用m來表示,或者簡單地用一個下劃線「」來作前綴。移山公司規定下劃線通常不用在其餘方面
由多個單詞組成的變量名,若是所有都是小寫,很不易讀,一個簡單的解決方案就是用大小寫區分它們。
一個通用的作法是:
須要註釋什麼?不要註釋程序是怎麼工做的(How),程序自己就應該能說明這一問題
//this loop starts the i from0 to len, in each step, it // does SomeThing for (i =0; i < len; i++) { DoSomeThing(); }
以上的註釋是多餘的。註釋是爲了解釋程序作什麼(What),爲何這樣作(Why),以及要特別注意的地方,以下
/go thru the array, note the last element is at [len-1] for (i =0; i < len; i++) { DoSomeThing(); }
複雜的註釋應該放在函數頭,不少函數頭的註釋都用來解釋參數的類型等,若是程序正文已經可以說明參數的類型in/out,就不要重複!
註釋也要隨着程序的修改而不斷更新,一個誤導的(Misleading)註釋每每比沒有註釋更糟糕
另外,註釋(包括全部源代碼)應該只用ASCII字符,不要用中文或其餘特殊字符,不然會極大地影響程序的可移植性。
在現代編程環境中,程序編輯器能夠設置各類美觀得體的字體,咱們可使用不一樣的顯示風格來表示程序的不一樣部分。
注意:有些程序設計語言的教科書對於基本的語法有詳細的註釋,那是爲了教學的目的,不宜在正式項目中也這麼作
代碼設計規範不光是程序書寫的格式問題,並且牽涉到程序設計、模塊之間的關係、設計模式等方方面面,這裏又有很多內容與具體程序設計語言息息相關(如C、C++、Java、C#),可是也有通用的原則,這裏主要討論通用的原則。若是你只想爲了「爽」而寫程序,那麼能夠忽略下面的原則;若是你寫的程序會被不少人使用,而且你得加班調試本身的程序,那最好仍是遵照下面的規定
現代程序設計語言中的絕大部分功能,都在程序的函數(Function、Method)中實現。關於函數,最重要的原則是:只作一件事,而且要作好
函數最好有單一的出口,爲了達到這一目的,可使用goto。只要有助於程序邏輯的清晰體現,什麼方法均可以使用,包括goto
當程序的主要功能實現後,一些程序員會樂觀地估計只須要另外20%的時間,給代碼加一些錯誤處理就大功告成了,可是這20%的工做每每須要所有項目80%的時間
1. 參數處理
在Debug版本中,全部的參數都要驗證其正確性。在正式版本中,對從外部(用戶或別的模塊)傳遞過來的參數,要驗證其正確性
2. 斷言
如何驗證正確性?那就要用斷言(Assert)。斷言和錯誤處理是什麼關係?當你以爲某事確定如什麼時候,就能夠用斷言。
注意,除了關於異常(Exception)的部分,大部分其餘原則對C#也適用
1. 類
2. class vs. struct
3. 公共/保護/私有成員(public、protected和private)
4. 數據成員
5. 虛函數(Virtual Function)
使用虛函數來實現多態(Polymorphism)
僅在頗有必要時,才使用虛函數
若是一個類型要實現多態,在基類(Base Class)中的析構函數應該是虛函數
6. 構造函數(Constructors)
7. 析構函數(Destructor)
8. new和delete
若是可能,實現本身的new/delete,這樣能夠方便地加上本身的跟蹤和管理機制。本身的new/delete能夠包裝系統提供的new/delete
檢查new的返回值。new不必定都成功
釋放指針時不用檢查NULL
9. 運算符(Operators)
在理想狀態下,咱們定義的類不須要自定義操做符。確有必要時,纔會自定義操做符
運算符不要作標準語義以外的任何動做。例如,「==」的判斷不能改變被比較實體的狀態
運算符的實現必須很是有效率,若是有複雜的操做,應定義一個單獨的函數
當你拿不定主意的時候,用成員函數,不要用運算符
10. 異常(Exceptions)
異常是在「異乎尋常」的狀況下出現的,它的設置和處理都要花費「異乎尋常」的開銷,因此不要用異常做爲邏輯控制來處理程序的主要流程
瞭解異常及處理異常的花銷,在C++語言中,這是不可忽視的開銷
當使用異常時,要注意在什麼地方清理數據
異常不能跨過DLL或進程的邊界來傳遞信息,因此異常不是萬能的
11. 類型繼承(Class Inheritance)
僅在必要時,才使用類型繼承
用const標註只讀的參數(參數指向的數據是隻讀的,而不是參數自己)
用const標註不改變數據的函數
代碼複審的正肯定義:看代碼是否在「代碼規範」的框架內正確地解決了問題
名稱 | 形式 | 目的 |
---|---|---|
自我複審 | 本身 vs 本身 | 用同伴複審的標準來要求本身。不必定最有效,由於開發者對本身老是過於自信。若是能鍥而不捨,則對我的有很大好處 |
同伴複審 | 複審者 vs 開發者 | 簡便易行 |
團隊複審 | 團隊 vs 開發者 | 有比較嚴格的規定和流程,適用於關鍵的代碼,以及複審後再也不更新的代碼覆蓋率高——不少雙眼睛盯着程序,但效率可能不高(全體人員都要到會) |
軟件工程中最基本的複審手段,就是同伴複審
1. 代碼複審的目的在於:
找出代碼的錯誤,好比:
編碼錯誤,好比一些碰巧騙過了編譯器的錯誤
不符合團隊代碼規範的地方
發現邏輯錯誤,程序能夠編譯經過,可是代碼的邏輯是錯的
發現算法錯誤,好比使用的算法不夠優化,邊界條件沒有處理好等
發現潛在的錯誤和迴歸性錯誤—當前的修改致使之前修復的缺陷又從新出現
發現可能須要改進的地方
教育(互相教育)開發人員,傳授經驗,讓更多的成員熟悉項目各部分的代碼,同時熟悉和應用領域相關的實際知識
喜歡在程序中加一些特定的標記,來跟蹤各類「要作的事情」,這些標記最好是加上人名,以示負責,
在代碼複審過程當中,$review
標記的問題要一一討論,在代碼複審事後,全部的 $review
標記要清除。在一個里程碑或正式版本發佈以前,全部的 $todo
和 $bug
標記都要清除
1. 概要部分
代碼符合需求和規格說明麼?
代碼設計是否考慮周全?
代碼可讀性如何?
代碼容易維護麼?
代碼的每一行都執行並檢查過了嗎?
2. 設計規範部分
設計是否聽從已知的設計模式或項目中經常使用的模式?
有沒有硬編碼或字符串/數字等存在?
代碼有沒有依賴於某一平臺,是否會影響未來的移植(如Win32到Win64)?
開發者新寫的代碼可否用已有的Library/SDK/Framework中的功能實現?在本項目中是否存在相似的功能能夠調用而不用所有從新實現?
有沒有無用的代碼能夠清除?
(不少人想保留儘量多的代碼,由於之後可能會用上,這樣致使程序文件中有不少註釋掉的代碼,這些代碼均可以刪除,由於源代碼控制已經保存了原來的老代碼。)
3. 代碼規範部分
4. 具體代碼部分
有沒有對錯誤進行處理?對於調用的外部函數,是否檢查了返回值或處理了異常?
參數傳遞有無錯誤,字符串的長度是字節的長度仍是字符(多是單/雙字節)的長度,是以0開始計數仍是以1開始計數?
邊界條件是如何處理的?switch語句的default分支是如何處理的?循環有沒有可能出現死循環?
有沒有使用斷言(Assert)來保證咱們認爲不變的條件真的獲得知足?
對資源的利用,是在哪裏申請,在哪裏釋放的?有無可能存在資源泄漏(內存、文件、各類GUI資源、數據庫訪問的鏈接,等等)?有沒有優化的空間?
數據結構中有沒有用不到的元素?
5. 效能
代碼的效能(Performance)如何?最壞的狀況是怎樣的?
代碼中,特別是循環中是否有明顯可優化的部分(C++中反覆建立類,C#中 string 的操做是否能用StringBuilder來優化)?
對於系統和網絡的調用是否會超時?如何處理?
6. 可讀性
代碼可讀性如何?有沒有足夠的註釋?
7. 可測試性
針對特定領域的開發(如數據庫、網頁、多線程等),能夠整理專門的核查表
在開發層次,結對編程能提供更好的設計質量和代碼質量,兩人合做解決問題的能力更強
對開發人員自身來講,結對工做能帶來更多的信心,高質量的產出能帶來更高的知足感
在企業管理層次上,結對能更有效地交流,相互學習和傳遞經驗,分享知識,能更好地應對人員流動
總之,若是運用得當,結對編程能夠取得更高的投入產出比(Return of Investment)
駕駛員:寫設計文檔,進行編碼和單元測試等XP開發流程
領航員:審閱駕駛員的文檔、駕駛員對編碼等開發流程的執行;考慮單元測試的覆蓋率;思考是否須要和如何重構;幫助駕駛員解決具體的技術問題
駕駛員和領航員不斷輪換角色,不要連續工做超過一小時,每工做一小時休息15分鐘。領航員要控制時間
主動參與。任何一個任務都首先是兩我的的責任,也是全部人的責任。沒有「個人代碼」、「你的代碼」或「他/她的代碼」,只有「咱們的代碼」
只有水平上的差距,沒有級別上的差別。兩人結對,儘管可能你們的級別資歷不一樣,但無論在分析、設計或編碼上,雙方都擁有平等的決策權利
設置好結對編程的環境,座位、顯示器、桌面等都要能容許兩我的溫馨地討論和工做。若是是經過遠程結對編程,那麼網絡、語音通信和屏幕共享程序要設置好