最近拜讀了《代碼大全》,這部大塊頭確實經典,涉及到了軟件開發的方方面面。有點后悔沒有早些閱讀,值得推薦給還沒讀過的朋友。它並不是針對某種語言的武林秘籍,應該可以看作是基礎內功修煉吧。
筆記比較簡略,只供簡單查閱~
一、開發前期准備相關
1.需求階段:
- 發現錯誤的時間要盡量接近進入該錯誤的時間。由於需求是首先進行的活動,所以需求階段引入的缺陷可能在系統中潛伏的時間最長,代價也更昂貴。
- 如果沒有一個良好的問題定義,你努力解決的可能是一個錯誤的問題。
- 明確的需求有助於確保用戶駕馭系統的能力;
- 明確的需求有助於避免爭論。
- 重視需求有助於減少開始編程開發之后的系統變更情況。
- 一個形象的比喻:需求像水。如果凍結了,就容易在上面開展建設。
- 開發過程能夠幫助客戶更好的理解自己的需求,這往往是需求變更的主要來源。
2.架構
- 架構應詳細定義所需的主要的類。瞄准80/20准則:對構成系統的80%功能的20%的類進行詳細說明。
- 架構應建立一套有關錯誤消息的約定。
- 架構的總體質量:
- 《人月神話》的中心論題,說的就是大型系統的本質問題是維持其“概念完整性”。
- 好的架構設計應該與待解決的問題和諧一致。
- 架構應該描述所有主要決策的動機。
- 優秀的軟件架構很大程度上是與編程語言無關的。
3.構建決策
- 深入一種語言去編程
- 確定你在技術浪潮中的位置,並相應調整計划和預期目標。
二、編寫高質量代碼
1.類
軟件的首要技術使命就是管理復雜度。可以通過把整個復雜系統分解為多個子系統降低問題的復雜度。
關於封裝:類很像是冰山,八分之七都在水面以下,你只能看到水面以上的八分之一。
抽象數據類型(ADT)是指一些數據以及在這些數據上所能進行的操作的集合。
考慮類的一種方式,就是把它看作抽象數據類型,再加上繼承和多態兩個概念。
警惕有超過7個數據成員的類。
盡量使用多態,避免的大量的類型檢查。
構造函數:
- 如果可能,應該在所有的構造函數中初始化所有的數據成員。
- 用私有構造函數實現單件屬性。
2.子程序
- 子程序是為實現特定的目的而編寫的一個可被調用的方法或過程。函數是有返回值的子程序;過程是沒有返回值的子程序。
- 合理的參數個數,上線大概在7個左右。
3.防御式編程
- 主要思想:子程序不應傳入錯誤數據而被破壞,哪怕是其他子程序產生的錯誤數據。
- 在代碼中保留多少防御式代碼?
- 保留那些檢查重要錯誤的代碼;
- 去掉檢查細微錯誤的代碼;
- 為技術支持人員記錄錯誤信息;
- 確保留在代碼中的錯誤信息是友好的。
4.偽代碼創建子過程
- 用類似英語的語句描述特定的具體操作;
- 避免使用目標編程語言中的語法元素;
- 在意圖的層面編寫偽代碼;
- 在一個足夠低的層次上編寫偽代碼,以便於近乎自動地從他生成代碼,然后把它編程代碼中的注釋。
三、變量
1.使用變量的一般事項
2.變量命名
- 為變量命名時要考慮的重要事項是,該名字要完全、准確地描述出該變量所代表的事物。
- 較長的名字適用於較少使用的變量或者全局變量;較短的名字適用於局部變量或者循環變量。
- 代碼閱讀的次數要遠遠大於編寫的次數。要確保你所取的名字更側重於閱讀方便而不是編寫方便。
3.特定數據類型命名
①.限定詞命名
- 如果變量名中含有Total,Average,Sum,Max等限定詞,請記住把限定詞加在變量名的最后。
- 用Count和Index代替Num
關於Num限定詞容易產生歧義,例如:numStudents表示學生數,studentNum表示學生序號。為了避免這種歧義,可以用Count和Index來代替,比如用studentCount表示學生總數,而用studentIndex表示學生序號。
②.為循環索引命名
- 如果一個變量要在循環之外使用,那么就要使用一個i,j,k之外更有意義的名字。
- 如果循環不是只有幾行,那么讀者很容易忘記i本來的含義,因此最好給循環下標取一個比i更有意義的名字。
③.為狀態變量命名
為狀態變量取一個比flag更好的名字。標記一般使用枚舉類型、具名常量或者用作具名常量的全局變量來對其賦值。
④.為布爾變量命名
- 謹記典型的布爾變量名:found,error,success,ok。
- 給布爾變量賦予隱含“真/假”含義的名字,而且可以省略變量名開頭的Is前綴。
- 使用肯定的變量名。
⑤.為具名常量命名
應該根據該常量所表示的含義,而不是該常量所具有的數值來命名。
四、表驅動法
- 表驅動法是一種編程模式,從表里面查找信息而不使用邏輯語句(if和case)。
- 表提供了一種復雜邏輯和繼承結構的替代方案。
五、一般控制方法
1.布爾表達式
- 拆分復雜的判斷而引入新的變量;
- 把復雜的表達式做成布爾函數;
- 用決策表代替復雜的條件。
2.按照數軸的順序編寫數值表達式;
3.將復雜度降低到最低是編寫高質量代碼的關鍵。
六、代碼改善
1.軟件質量的普遍原理就是改善質量以降低開發成本。
2.提高生產效率和改善質量的最佳途徑就是減少花在代碼返工上的時間,無論返工是由需求、設計改變還是調試引起的。
3.結對編程,通過復查可以快速地將所有開發者的水平提高到最高優秀的開發者的高度。
七.開發者測試
1.白盒測試指的是測試者清楚對象內部工作機制的測試,測試自己開發的程序應該使用這種測試方式。
2 測試的特性:
- 測試的目標與其他測試活動背道而馳,測試的目的是找出錯誤。
- 測試永遠不可能證明程序中徹底沒有錯誤。
- 測試的結果是軟件質量的一個指示器,但是測試本身並不能改善軟件質量,這種妄想就像天天通過稱體重來減肥一樣。假如希望改進你的軟件質量,僅用更多的測試是沒用的,你需要的是更高質量的開發。
3 推薦開發者的測試方式
- 對每一項相關的需求進行測試,以確保需求都已經被實現;
- 基礎測試和數據流測試;
- 使用一個檢查表,記錄你在本項目中迄今為止所犯的,以及在過去的項目中中所犯的錯誤類型,這有助於“猜測錯誤”的准確性;
- 推薦測試先行。
4 測試技巧錦囊
- 結構化的基礎測試
- 需要去測試程序中的每行代碼至少一次。
- 所需基礎測試最少用例數量的計算方式:
對通過子程序的直路,開始記為1;
遇到以下關鍵字時,加1,比如:if,and,or,while,repeat,for;
遇到每個case語句,加1,如果case語句沒有默認語句,再加1.
5.測試數據生成器
- 為了系統的對程序的某些部分進行測試,你可能會寫一些代碼。
- 正確設計的測試數據生成器能產生意想不到的、不尋常的測試用例;
- 比起手工構造測試數據,數據生成器能夠更加徹底地對程序進行測試。
八、調試
①關於調試
- 理解你正在編寫的程序;
- 明確你犯了那種類型的錯誤;
- 從代碼閱讀者的角度分析代碼質量;
- 審視自己解決問題的方法,花點時間來分析並改善你的調試方法,可能就是減少程序開發時間的最好方法;
- 審視自己修改正缺陷的方法。
②尋找缺陷步驟
- 把錯誤狀態穩定下來,也就是能讓缺陷穩定的重現,這幾乎是最有挑戰的工作之一;
- 確定錯誤的來源;
- 修補缺陷;
- 對修補的缺陷進行測試;
- 查找是否還有類似的錯誤。
③修復缺陷
- 在動手之前先要理解問題,知道你能真正理解問題,每次都能正確地預測結果為止;
- 理解程序本身,而不僅僅是問題;
- 驗證對錯誤的分析;
- 放松一下;
- 保存最初的源代碼,至少你能對新舊代碼進行比較,看到底改了哪些地方;
- 治本,而不是治標;
- 修改代碼時一定要有恰當的理由;
- 一次只做一個改動;
- 檢查自己的改動;
- 增加暴露問題的單元測試;
- 搜索類似的缺陷,如果你想不出如何查找類似缺陷,這就意味着你還沒有完全理解問題。
④調試工具
- 源代碼比較工具;
- 編譯器的警告信息,把編譯器的警告級別設置為最嚴格;
- 增強的語法檢查和邏輯檢查;
- 執行性能剖測器;
- 測試框架;
- 調試器;
九、重構
1.軟件演化類型
- 在修改中軟件的質量要么提高,要么惡化。
- 軟件演化的基本原則就是,演化應當提高程序的內在質量。
2.重構的理由
- 代碼重復:DRY,Do not Repeat Yourself;
- 冗長的子程序:很少會用到長度超過一個屏幕的子程序。改善方法是提高其模塊性-增加定義完善、命名准確的子程序,讓他們各自集中力量做好一件事;
- 循環過長或嵌套過深;
- 內聚性太差的類;
- 擁有太多參數的參數列表;
- 變化導致多個類的相同修改;
- 同時使用的相關數據並未以類的形式組織;
- 過多使用基本數據類型;
- 某個類無所事事;
- 中間人對象無事可做;
- 子程序明明不恰當。只要看到某個子程序命名有問題,就應該立即着手修改。
- 注釋被用於解釋難懂的代碼。不要為拙劣的代碼編寫文檔——應當重寫代碼;
- 程序中的一些代碼似乎是在將來的某個時候才會用到。對未來需求有所准備的代碼並不是編寫大量空中樓閣式的代碼,而是盡可能將滿足當前需求的代碼清晰明白的表現出來,使未來的程序員理解這些代碼到底完成了什么功能,沒完成什么功能,以便根據他們的需求進行調整。
3. 數據級的重構
- 用具名常量代替神秘數值;
- 使變量的名字更加清晰且傳遞更多信息;
- 用函數代替表達式;
- 將基礎類型轉化為類。如果一個基礎類型需要額外的功能或者額外的數據,那么就應該把基礎類型轉化為類,並添加你所需要的行為;
- 將一組類型碼轉換為類或者枚舉類型;
4.語句級的重構
- 分解布爾表達式
- 將復雜布爾表達式轉換為命名准確的布爾函數;
- 在嵌套的if-then-else語句中應該一知道答案就返回,而不是賦值給一個返回值
5.子程序級重構
- 提取子程序或者方法
- 將冗長的子程序轉換為類
- 合並相似的子程序,通過參數區分他們的功能
6 類接口的重構
- 將成員函數放到另一個類中
- 將一個類變為兩個類。如果一個類中的成員函數完成兩種截然不同的功能,將這樣的類轉換為兩個類
- 對於不能修改的類成員去掉其set函數。如果一個成員在對象被創建時設置,之后遍不再允許修改,那么刪除其set函數,而是在構造函數中進行初始化。
7 系統級重構
- 使用工廠函數而不是簡單的構造函數;
- 用異常代替錯誤代碼,或者反其道而行之,取決於你的錯誤處理策略,請確保代碼使用了標准的錯誤處理方法。
8. 安全的重構
- 保存初始代碼。要保證你還能回到代碼的初始狀態。
- 重構的步伐請小些。這樣才能理解所做修改對程序的全部影響。
- 同一時間只做一項重構。在進入到下一項重構之前,對代碼進行重新編譯和測試。
- 把要做的事情一步步列出來。寫出一份重構列表能夠讓你在修改時保持思路連貫。
- 設置一個停車場。把你需要在未來進行而現在可以暫且放在一遍的修改工作記錄下來。
- 利用編譯器警告信息。把編譯器的警告級別設置為最嚴格。
- 重新測試。應該把重新測試作為修改代碼工作的補充。
- 檢查對代碼的修改。如果說第一次運行程序時檢查代碼是必要的,那么接下來修改代碼的過程中,時刻檢查代碼則更為必要。另外,應該把簡單的修改當作復雜的修改對待。
- 根據風險級別來調整重構方法。尤其是對於有一定風險的重構,謹慎才能避免出錯。
9.重構策略
- 在增加子程序的時候重構
- 在添加類的時候重構
- 在修補缺陷的時候重構
- 關注於易出錯的模塊
- 關注高度復雜的模塊
- 在維護環境下,改善你手中正在處理的代碼。如果你正在維護某部分代碼,請確保代碼在離開你的時候比來之前更健康
- 開發階段的重構是提高程序質量的最佳時機。
十、代碼調整技術(性能)
1.邏輯
- 在知道答案后停止判斷;
- 按照出現頻率調整判斷順序,讓運行最快和判斷為真可能性的判斷首先執行;
2.循環
- 將判斷外提。如果在循環運行時某個判斷結果不會改變,你就可以把這個判斷提到循環的外面。
- 合並。就是把兩個對同一組元素進行循環的操作合並在一起,減少循環開銷。
- 盡可能減少在循環內部做的事情。如果可以在循環外面計算某些語句,而只在循環內部使用計算結果,那么就把該部分語句放在循環外面。
十一、管理構建
1.鼓勵良好的編碼實踐幾種技術
- 給項目的每一部分分派兩個人
- 逐行復查代碼
- 要求高級技術人員給代碼簽名
- 安排一些好的代碼示例給別人看
- 強調代碼是公共財產
- 獎勵好代碼
- 一份簡單的標准:“我必須能理解並閱讀這個項目里的所有代碼”
2.編程工具
①源代碼工具
- IDE
- diff比較器
- merge工具
- 源代碼美化器
- 代碼模板
②重構源代碼
重構器
程序庫:
代碼生成器
打造自己的編程工具
十二、源代碼布局與風格
1.基本原則
- 好布局方案的關鍵是能使程序的外觀與邏輯結構一致,也就是讓人和計算機有同樣的理解。
- 編程工作量的一小部分是寫讓計算機能看懂的程序,一大部分是讓其他人能看懂程序。
- 外表悅目比其他指標是最不重要的。
2.良好布局的目標
- 程序表現代碼的邏輯結構
- 始終如一的表現代碼的邏輯結構
- 改善可讀性
- 經得起修改
3.布局技術
- 空白。能提高可讀性,如空格、制表符、換行、空行。
- 分組。確保相關的語句成組放在一起。
- 空行。將不相關的語句分隔開。
- 縮進。用縮進的形式顯示程序的邏輯結構。
- 括號。對包含兩個以上的項的表達式,應該用括號去澄清。
4.控制結構的布局
- 不要用未縮進的begin-end對
- 段落之間要使用空行
- 單語句代碼段的格式要前后統一
- 對於復雜的表達式,將條件分隔放在幾行上
5.單行語句的布局
- 每行只放一個語句
- 每個語句只進行一個操作
- 數據聲明的布局:
-
- 每行只聲明一個變量
- 變量聲明應盡量接近其首次使用的位置
6.注釋的布局
- 注釋的縮進要與相應代碼一致
- 每行注釋至少用一個空行分開
7.子程序的布局
- 用空行分隔子程序的各部分
- 將子程序參數按照標准縮進
8.類的布局
- 一個文件應只有一個類
- 文件的命名應與類名有關
- 在文件中清晰的分隔子程序,至少使用兩個空行
十三、自說明代碼
1.編程風格做文檔
在代碼層文檔中起作用的因素並不是注釋,而是好的編程風格。對於精心編寫的代碼而言,注釋只是精美外衣的小飾物而已。
代碼易讀性的最高水平:自說明代碼。
2.高效注釋之關鍵
- 采用不會打斷或抑制修改的注釋風格
- 用高層次的偽代碼減少注釋時間
- 將注釋集成到你的開發風格中
3.注釋技術
①行尾注釋
- 不要對單行代碼做行尾注釋
- 行尾注釋可用於數據聲明
②代碼塊注釋
- 注釋應表達代碼的意圖
- 注釋代碼時應注重 為何做(why) 而不是 怎么做(how)
- 用注釋為后面的內容做鋪墊
- 說明非常規做法。
- 將主次注釋區分開。在次要注釋前加省略號,主要注釋以正常方式編排
③注釋聲明數據
對數值聲明的注釋應標明其單位;
十四、軟件工藝的話題
1.征服復雜度:
軟件設計與構建的 主要目標就是征服復雜性。
2.代碼可讀性
首先是為人寫程序,其次才是為機器
3.深入一門語言去編程,而不是浮於表面
4.借助規范集中注意力
規范是一套用於管理復雜度的智力工具。
5.基於問題域編程
- 盡可能工作於最高的抽象層次。
- 將程序划分為不同的抽象層次。