《構建執法》閱讀筆記之四

1. 代碼規範

每一個人對於什麼是「好」的代碼規範未必認同,這時咱們頗有必要給出一個基準線—什麼是好的代碼規範和設計規範java

計算機只關心編譯生成的機器碼,你的程序採用哪一種縮進風格,變量名有無統一的規範等,與機器碼的執行無關。可是,作一個有商業價值的項目,或者在團隊裏工做,代碼規範至關重要。「代碼規範」能夠分紅兩個部分:mysql

1. 代碼風格規範——主要是文字上的規定,看似表面文章,實際上很是重要程序員

2. 代碼設計規範——牽涉到程序設計、模塊之間的關係、設計模式等方方面面的通用原則算法


 

2. 代碼風格規範

代碼風格的原則是:簡明易讀無二義性
提示:這裏談的風格是一家之言,如遇爭執,關鍵是要本着「保持簡明,讓代碼更容易讀」的原則,看看爭執中的代碼規範可否讓程序員們更好地理解和維護程序sql

2.1 縮進

是用Tab鍵好,仍是二、四、8個空格?
結論4個空格,在Visual Studio和其餘的一些編輯工具中均可以定義Tab鍵擴展成爲幾個空格鍵。不用Tab鍵的理由是,Tab鍵在不一樣的狀況下會顯示不一樣的長度,嚴重干擾閱讀體驗。4個空格的距離從可讀性來講,正好數據庫

2.2 行寬

行寬必須限制,可是之前有些文檔規定的80字符行寬過小了(之前的計算機/打字機顯示行寬爲80字符),如今時代不一樣了,能夠限定爲100字符編程

2.3 括號

在複雜的條件表達式中,用括號清楚地表示邏輯優先級設計模式

2.4 斷行與空白的{ }行

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(); }

2.5 分行

不要把多條語句放在一行上

a =1; b =2;  // bogus if (fFoo) Bar();  // bogus

更嚴格地說,不要把多個變量定義在一行上

Foo foo1, foo2;    // bogus

2.6 命名

用單個字母給有複雜語義的實體命名並不可取,也是通過了實踐檢驗的方法叫「匈牙利命名法」。例如:

fFileExist  // 代表是一個bool值,表示文件是否存在; szPath // 代表是一個以0結束的字符串,表示一個路徑

2.7 下劃線

下劃線用來分隔變量名字中的做用域標註和變量的語義,如:一個類型的成員變量一般用m來表示,或者簡單地用一個下劃線「」來作前綴。移山公司規定下劃線通常不用在其餘方面

2.8 大小寫

由多個單詞組成的變量名,若是所有都是小寫,很不易讀,一個簡單的解決方案就是用大小寫區分它們。

  • Pascal——全部單詞的第一個字母都大寫
  • Camel——第一個單詞所有小寫,隨後單詞隨Pas-cal形式,這種方式也叫lowerCamel

一個通用的作法是:

  • 全部的類型/類/函數名都用Pascal形式,全部的變量都用Camel形式
  • 類/類型/變量:名詞或組合名詞,如Member、ProductInfo等
  • 函數則用動詞或動賓組合詞來表示,如get/set、RenderPage()

2.9 註釋

須要註釋什麼?不要註釋程序是怎麼工做的(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字符,不要用中文或其餘特殊字符,不然會極大地影響程序的可移植性。
在現代編程環境中,程序編輯器能夠設置各類美觀得體的字體,咱們可使用不一樣的顯示風格來表示程序的不一樣部分。

注意:有些程序設計語言的教科書對於基本的語法有詳細的註釋,那是爲了教學的目的,不宜在正式項目中也這麼作


 

3. 代碼設計規範

代碼設計規範不光是程序書寫的格式問題,並且牽涉到程序設計、模塊之間的關係、設計模式等方方面面,這裏又有很多內容與具體程序設計語言息息相關(如C、C++、Java、C#),可是也有通用的原則,這裏主要討論通用的原則。若是你只想爲了「爽」而寫程序,那麼能夠忽略下面的原則;若是你寫的程序會被不少人使用,而且你得加班調試本身的程序,那最好仍是遵照下面的規定

3.1 函數

現代程序設計語言中的絕大部分功能,都在程序的函數(Function、Method)中實現。關於函數,最重要的原則是:只作一件事,而且要作好

3.2 goto

函數最好有單一的出口,爲了達到這一目的,可使用goto。只要有助於程序邏輯的清晰體現,什麼方法均可以使用,包括goto

3.3 錯誤處理

當程序的主要功能實現後,一些程序員會樂觀地估計只須要另外20%的時間,給代碼加一些錯誤處理就大功告成了,可是這20%的工做每每須要所有項目80%的時間

1. 參數處理
在Debug版本中,全部的參數都要驗證其正確性。在正式版本中,對從外部(用戶或別的模塊)傳遞過來的參數,要驗證其正確性

2. 斷言
如何驗證正確性?那就要用斷言(Assert)。斷言和錯誤處理是什麼關係?當你以爲某事確定如什麼時候,就能夠用斷言。

3.4 如何處理C++中的類

注意,除了關於異常(Exception)的部分,大部分其餘原則對C#也適用

1. 類

  • 使用類來封裝面向對象的概念和多態(Poly-morphism)
  • 避免傳遞類型實體的值,應該用指針傳遞。換句話說,對於簡單的數據類型,沒有必要用類來實現
  • 對於有顯式的構造和析構函數的類,不要創建全局的實體,由於你不知道它們在什麼時候建立和消除
  • 僅在必要時,才使用「類」

2. class vs. struct

  • 若是隻是數據的封裝,用struct便可

3. 公共/保護/私有成員(public、protected和private)

  • 按照這樣的次序來講明類中的成員:public、protected、private

4. 數據成員

  • 數據類型的成員用m_name說明
  • 不要使用公共的數據成員,要用inline訪問函數,這樣可兼顧封裝和效率

5. 虛函數(Virtual Function)

  • 使用虛函數來實現多態(Polymorphism)

  • 僅在頗有必要時,才使用虛函數

  • 若是一個類型要實現多態,在基類(Base Class)中的析構函數應該是虛函數

6. 構造函數(Constructors)

  • 不要在構造函數中作複雜的操做,簡單初始化全部數據成員便可
  • 構造函數不該該返回錯誤(事實上也沒法返回)。把可能出錯的操做放到HrInit()或FInit()中。

7. 析構函數(Destructor)

  • 把全部的清理工做都放在析構函數中。若是有些資源在析構函數以前就釋放了,記住要重置這些成員爲0或NULL
  • 析構函數也不該該出錯

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. 可測試

  • 代碼是否須要更新或建立新的單元測試?

針對特定領域的開發(如數據庫、網頁、多線程等),能夠整理專門的核查表


 

結對編程

結對編程的優點

  1. 在開發層次,結對編程能提供更好的設計質量和代碼質量,兩人合做解決問題的能力更強

  2. 對開發人員自身來講,結對工做能帶來更多的信心,高質量的產出能帶來更高的知足感

  3. 在企業管理層次上,結對能更有效地交流,相互學習和傳遞經驗,分享知識,能更好地應對人員流動

總之,若是運用得當,結對編程能夠取得更高的投入產出比(Return of Investment)


結對編程步驟

    1. 駕駛員:寫設計文檔,進行編碼和單元測試等XP開發流程

    2. 領航員:審閱駕駛員的文檔、駕駛員對編碼等開發流程的執行;考慮單元測試的覆蓋率;思考是否須要和如何重構;幫助駕駛員解決具體的技術問題

    3. 駕駛員和領航員不斷輪換角色,不要連續工做超過一小時,每工做一小時休息15分鐘。領航員要控制時間

    4. 主動參與。任何一個任務都首先是兩我的的責任,也是全部人的責任。沒有「個人代碼」、「你的代碼」或「他/她的代碼」,只有「咱們的代碼」

    5. 只有水平上的差距,沒有級別上的差別。兩人結對,儘管可能你們的級別資歷不一樣,但無論在分析、設計或編碼上,雙方都擁有平等的決策權利

    6. 設置好結對編程的環境,座位、顯示器、桌面等都要能容許兩我的溫馨地討論和工做。若是是經過遠程結對編程,那麼網絡、語音通信和屏幕共享程序要設置好

相關文章
相關標籤/搜索