第一部分 打好基礎 Laying the Foundation
- 第一章 歡迎進入軟件構建的世界 Welcome to Software Construction
- 什麼是軟件的構建
- 定義問題
- 需求分析
- 規劃構建
- 軟件架構 或者 高層設計
- 詳細設計
- 編碼與調試
- 單元測試
- 集成測試
- 集成
- 系統測試
- 保障維護
- 總結
- 軟件構建是軟件開發中惟一不可缺乏的部分,也就是必須完成的部分
- 軟件的構建主要包括:詳細設計、編碼調試、集成和開發者測試(單元測試和集成測試)
- 你對軟件構建的理解程度決定 程序員的優秀程度
- 第二章 用隱喻來更充分地理解軟件開發 Metaphors for a Richer Understanding of Software Development
- 第三章 三思然後行:前期準備 Measure Twice, Cut Once: Upstream Prerequisites
- 第四章 關鍵的『構建』決策 Key Construction Decisions
第二部分 建立高質量的代碼 Creating-High Quality Code
- 第五章 軟件構建中的設計 Design in Construction
- 第六章 能夠工做的類 Working Classes:抽象是以簡化方式看待複雜操做的能力
- 6.1類的基礎:抽象數據類型 ADTs
- ADT,abstract data type 抽象數據類型。它指的是一些數據及對這些數據的操做的集合。這裏的"數據",不只僅是數學上或者軟件工程中的數據,而是現實世界中能夠操做的實體
- ADT 的好處:
- 隱藏實現細節(可能會有後續操做)
- 容易改動(更改數據結構,優化提升性能)
- 讓接口提供更多信息(經過名稱)
- 可讀性提升
- 不須要屢次傳值(相關操做須要用到變量都放在ADT裏面了)
- 把常見的底層數據類型(棧 隊列)建立爲ADT並使用
如出場演員名單(底層數據類型是列表)java
- 對於應用層面上ADT,最好在原有ADT的基礎上建立一個針對現實世界問題的抽象層次。
- 簡單的事情能夠抽取成ADT(方便擴展後續操做)
- 在支持面向對象的語言,ADT能夠用本身的class(類)實現。class=ADT+繼承+多態
- 6.2良好的類接口:用接口去展現抽象,確保細節隱藏在抽象背後
- 接口中的每一個子程序都朝着這個一致的目標而工做
- 類的接口要展現一致的抽象層次,一個類只能實現一個ADT,否則就要拆分
- 要理解類要抽象出什麼功能,避免把使用的類庫或者容器類暴露出來
- 儘量讓接口可編程(programatic,編譯器強制要求),而不是表達語義(sematic,經過方法名和註釋)。
好比多個類的初始化有前後順序;一個類沒有初始化調用會報錯c++
- 擴展的時候要注意新增公用方法的 抽象的一致性
- 不要對類的使用者作任何假設,接口已經隱含了契約(接口已經提供了調用的條件說明)
- 語義上的封裝比語法上的封裝要困難(公用接口不要暴露內部實現和數據)
P142 不少例子程序員
- 封裝和抽象要麼二者皆有,要麼所有沒有
- 6.3有關設計和實現的問題:包含/繼承/成員函數/數據成員/類之間的耦合性
- 包含(has a "有一個的關係"):數據成員的限制:7-+2
數據成員都是基本數據類型,數據成員不超過9;數據成員都是複雜對象,數據成員不超過5算法
- 繼承(is a 「是一個的關係」)(使用時會增長複雜度,有違軟件的技術使命-管理複雜度的)
- 要考慮方法和屬性對派生類是否可見,方法是否要有默認的實現,是否能夠覆蓋?
- 繼承要符合里氏替換原則:對於基類定義的接口,在派生類的語義應該是相同的
- 不要覆蓋不可覆蓋的方法(不要新建一個與基類的private相同的方法)
- 只有一個派生類,可能犯了提早設計的毛病
- 繼承不要超過2-3層,派生類總數不超過該7+-2個;
- 儘量讓數據讓數據時private,由於繼承會破壞封裝
- 若是多個類共享數據而非行爲,建立這些類包含共用對象
- 若是多個類共享行爲而非數據,在基類定義接口,繼承基類
- 若是多個類共享行爲和數據,在基類定義接口和數據成員,繼承基類
- 當你想由基類控制接口時,用繼承,由本身控制接口,用包含
- 成員函數和數據成員:
- 減小如下數字的數量
- 所實例化對象的種類
- 調用實例化對象的子程序的數量
- 調用由其餘對象返回對象的子程序數量
- 子程序的數量
- 構造函數
- 6.4建立類的緣由
- 對現實對象的建模
- 對抽象對象的建模(如shape就是抽象對象,得出恰當的抽象對象很重要)
- 下降複雜度(調用類的接口不用關心實現細節)
- 隔離複雜度
- 隱藏實現細節
- 限制變化的影響範圍
- 隱藏全局數據
- 讓參數傳遞更流暢
- 建立中心控制點
- 讓代碼重用
- 爲程序族作規劃
- 把相關操做放在一塊兒(子程序的組合)
- 實現特定的重構
- 6.5與具體編程語言有關的問題
- 6.6超越類:包
- 類的質量覈對表P157-P158
- 第七章 高質量的子程序 High-Quality Routines
- 7.1 建立子程序(routines)的正當理由:提升程序管理能力,包括提升可讀性、可靠性和可修改性,節省代碼空間是次要,或者是反作用(side effect)。
- 下降複雜度
- 引入中間,易懂的抽象
- 避免重複
- 支持子類化(subclassing)
- 隱藏順序
- 隱藏指針操做
- 提升可移植性
- 簡化布爾判斷
- 改善性能
- 確保全部程序都很小
- 7.2 在子程序層上設計(Design at the Routine Level):抽象和封裝理念適合類層次的設計,內聚性適合子程序設計
- 內聚性(cohesion):指子程序中各類操做之間聯繫的緊密程度。(一個子程序就幹一個活)
- 功能內聚性:一個子程序只幹一件事情
- 如下是不夠理想的內聚性,可是有做用的。
- 順序上的內聚性:子程序包含按特定順序執行的操做,這些操做共享數據,並且只有在操做所有執行完畢的時候才達到一項完整的功能
- 通訊上的內聚性:指一個子程序內的不一樣操做用一樣的數據,但不存在任何的聯繫
- 臨時的內聚性:一些由於須要同時操做而放在一塊兒的操做
startUp()數據庫
- 如下是不可取的內聚性
- 過程內聚性:把一組操做放在子程序中並按照特定順序執行,除此以外沒有其餘彼此的聯繫
- 邏輯上內聚性(實際上是缺少邏輯的內聚性):把若干操做放入同一子程序,經過傳入的控制符執行不一樣的操做
- 若是子程序僅有由一系列if else語句以及其餘子程序語句組成,這樣的邏輯上內聚性的子程序是能夠的,就是這個子程序只發出各類指令,不進行任何的處理,那麼他就是一個事件處理器(event handler)
- 巧合的內聚性:子程序的操做之間沒有任何的 聯繫
- 7.3 好的子程序的名字 Good Routine Names
- 描述子程序所作的全部的事
- 避免使用無心義、模糊或者表達不清的動詞
event handling
事件處理除外
handleCalculation()
PerformServices()
OutputUser()
ProcessInput()
DealwithOutPut
編程
outPut1()
; outPut2()
小程序
- 根據需求肯定子程序的名稱長度
- 函數命名時要對函數的返回值有所描述
printer.isReady()
customerId.next()
數組
- 給子程序命名時要使用語氣強烈的動詞加賓語的形式,在面嚮對象語言中,不用再過程(Procedure)名中不用加入對象名(賓語)
document.print()
orderInfo.check()
安全
- 準確使用對仗詞
add/remove
insert/delete
start/stop
up/down
begin/end
increment/decrement
open/close
get/set
first/last
old/new
min/max
show/hide
create/destory
lock/unlock
source/target
get/put
ruby
- 爲經常使用操做確立命名規則
- 7.4 子程序能夠寫多長 How long can a Routine be
- 雖然有不少研究可是仍然沒有一個公認的說法,通常是50-200行左右,能夠從子程序的內聚性、嵌套層次、變量數量、決策點(desicions points)和註釋來決定子程序的長度,當超過200行,就要考慮在可讀性上問題了。
- 7.5 如何使用子程序參數 How to Use Routine Parameters
- 按照輸入-修改-輸出的順序排列參數:暗含了操做數據的順序
- 若是子程序用了相似的參數,參數的排列順序應該一致
- 使用全部的參數,沒有用的要撇除
- 把狀態或者出錯變量放在最後,由於它們只是程序的附屬
- 不要把子程序參數當作工做變量
對輸入參數進行操做,並將其做爲返回值返回結果。
* 在接口中對參數的假定加以說明,在接口文檔說明
* 參數是僅用於輸入,要被修改,仍是僅用於輸出
* 表示數量的參數單位(秒/分)
* 沒有用枚舉類型的話,應該說明狀態碼和錯誤碼的含義
* 所能接受的數據範圍
* 不能接受的特定數值
* 把子程序的參數個數限制在7個之內
* 如何一直須要傳遞不少參數,說明子程序之間的耦合太過緊密。若是須要向不少不一樣的子程序傳入相同的數據,就把這些子程序組成一個類,並把那些常用的數據做爲類的內部數據
* 考慮對參數使用某種輸入、修改、輸出的命名規則
> i_xxx
,m_xxx
,o_xxx
或者 Input_xxx
,Modify_xxx
,Output_xxx
* 爲子程序傳遞用以維持其接口抽象的變量和對象
* 假如常常須要修改子程序的參數表,且都是來自同一個對象,那就傳遞整個對象
* 若是隻是爲傳遞幾個特定的數據,把數據填入對象,再到子程序讀取這些數據,那就只傳遞數據的值
- 使用具名參數?
- 確保實際參數與形式參數相匹配?
- 7.6 使用函數時要特別考慮的問題
- 函數是有返回值的子程序,過程是指沒有返回值的子程序
- 設置函數的返回值
- 檢查全部可能的返回路徑
- 假如須要返回有關的數據,那就應該做爲類的成員保存起來,而不是做爲局部數據的引用或者指針返回。
- 7.7 宏子程序和內聯子程序 Macro Routine And Inline Routines?
- 把宏表達式整個都包含在括號內
- 把包含多條語句的宏用大括號括起來
- 用給子程序命名的方法給展開後代碼形同子程序的宏命名,以便須要能夠用子程序來替換宏。
- 高質量的子程序覈對表P185
- 第八章 防護式編程 Defensive Programming:子程序應該不因傳入錯誤的數據而被破壞,哪怕是由其餘子程序產生的錯誤的數據。即核心思想是程序都是有問題,都是要被修改。
- 8.1 保護程序免遭非法輸入數據破壞 Protecting your program from invalid inputs
- 檢查全部來源於外部數據的值:從文件、網絡、用戶或者其餘外部接口得到的數據應該檢查其有效性
- 檢查子程序全部輸入參數的值
- 決定如何處理錯誤的輸入數據
- 8.2 斷言 Assertions
- 在開發期間使用的,讓程序運行時進行自檢的代碼,一個斷言通常包含兩個參數,一個布爾表達式,一個斷言爲假時顯示的消息。斷言程序執行的前條件和後條件
- 用斷言檢查一下假定:
- 輸入參數或者輸出參數在預約範圍內
- 子程序開始(結束)文件或者流處於開啓或者關閉的狀態
- 子程序開始(結束)文件或者流讀寫的位置位於開頭或者結尾
- 指針不爲空
- 傳入子程序的數組或者其餘容器至少能存儲X個數據元素
- 表已經初始化,存儲着真實的數據
- 僅用於輸入的變量的值沒有被子程序改變
- 子程序開始或者結束的時候,某個容器爲滿或者爲空
- 高度優化的子程序與運行緩慢但邏輯清晰的子程序運行結果一致
- 對於高健壯性的代碼,應該先斷言再處理錯誤
- 8.3 錯誤處理技術 Erro-Handling Technique:斷言處理不該該發生的錯誤,錯誤處理技術處理預料中可能發生的錯誤。
- 返回中立值
- 數值計算返回0
- 字符串操做返回空字符串
- 指針操做返回空指針等
- 換用下一個正確的數據
溫度計讀取數值失敗,能夠等下一次如1/100秒讀取
* 返回前次相同的數據
> 上面溫度計的例子一樣適用
* 換用最接近的合法值
>獲得字符串長度小於0,那返回0
* 把警告信息寫到日誌
* 返回一個錯誤碼
* 調用處理錯誤的對象或者子程序
* 健壯性 vs 正確性 robustness vs correctness
* 高層次設計對錯誤處理方式的影響
- 8.4 異常Exceptions:是把錯誤或者異常事件傳遞給調用方代碼的特殊手段
- 用異常通知其餘的程序,發生了不可忽略的錯誤
- 與斷言類似,都是用來處理罕見甚至不可能出現的狀況???
- 是處理意外的有效途徑,可是增長複雜度
- 不能用異常來推卸責任,明確是由自身處理異常仍是由調用方處理異常
- 避免在解構函數和構造函數拋出異常,除非在同一地方進行捕獲
- 在恰當的抽象層次拋出異常,就異常應於當前接口的抽象層次一致,避免暴露實現細節和內部信息
- 避免使用空的catch語句,除非將catch的異常文檔化
- 瞭解函數庫可能拋出的異常、
- 考慮創造一個集中的異常報告機制,就是對於異常進行統一的格式化並記錄和存儲
- 把項目中對異常的使用標準化
- 能夠定義項目特定的異常類,記錄日誌、報告錯誤的集中起來和標準化
- 規定何種場合異常時須要局部處理
- 規定何種場合異常只能拋出,不能局部處理
- 考慮異常的替換方案
- 8.5 隔離程序,使之包容由程序錯誤形成的傷害Barricade Your Program to Contain the Damage Caused by Errors
- 隔欄是一種容錯的策略 damage containment strategy
- 能夠在類的層面上採用這種方法,在類的公有方法假定輸入的數據時不安全的,對數據進行檢查並清理,以後再將數據傳給私有方法,類的私有方法假定數據都是安全的
- 隔欄外部的程序使用錯誤處理技術,在那裏對數據的假定是不安全的。內部的程序使用斷言技術,這樣若是隔欄內的出現錯誤的數據,就是程序上的錯誤而非數據上的錯誤
- 8.6 輔助調試的代碼 Debugging aids
- 不要把自動地把產品版的限制強加於開發版上:開發版能夠花費更多的資源,容許運行緩慢,容許暴露不安全的操做
- 儘早引入輔助代碼
- 採用攻擊式編程 offensive programming
- 確保斷言使程序停止,及時修復錯誤
- case語句的default分支或者else分支產生嚴重的錯誤以至不被忽視
- 計劃移除調試輔助的代碼
- 8.7 肯定在產品中該保留多少防護式代碼 Determine How much Defensive Programming to Leave in Production code
- 保留那些檢查重要錯誤的代碼
- 去掉檢查細微錯誤的代碼
- 去掉能夠致使程序硬性崩潰的代碼:雖然便於調試,可是用戶體驗差
- 爲你的技術支持員記錄錯誤信息
- 確認留在代碼中的錯誤信息是友好的(friendly):就是要嚴謹,正式
- 8.8 對防護式編程採起防護姿態 Being Defensive about Defensive Programing
- 防護式編程增長複雜度
- 防護式編程由於要檢查參數使程序運行緩慢
- 覈對表 P211
- 第九章 僞代碼編程過程 The Pseudocode Programming Process
- 9.1 建立類和子程序的步驟概述 Summary steps of Building Classes and Routines
- 圖9.1 at p216:
- 建立一個類的步驟 Steps in Creating a class
- 建立類的整體設計 具體參考第六章 能夠工做類
- 定義類的職責
- 定義類要隱藏的"祕密"
- 定義類的接口所表明的抽象概念
- 決定這個類是否要從其餘類派生出來
- 決定這個類是否能夠被派生,便是否能被繼承
- 指出類的關鍵公用方法
- 標識並設計出類所須要的重要數據成員
- 建立類的子程序 Steps in Building a routine
- 圖9.2 at p217
- 子程序的種類:成員訪問子程序 (accessor routine ),轉發到其餘對象(pass-throughs)的子程序
- 步驟:設計子程序->檢查設計->編寫子程序的代碼->檢查代碼
- 複審並測試整個類 :在子程序建立的同時通過測試,在整個類能夠工做後,應該再對總體進行復查和測試,以便於發如今子程序獨立測試層次上沒法發現的問題
- 9.2 僞代碼 Pseudocode for Pros
- 僞代碼:描述 算法、子程序、類或者完整程序的工做邏輯、非正式的、相似英語的記法
- 僞代碼的注意事項:
- 用相似英語的語句來精確描述特定的操做
- 避免使用目標編程語言的元素,僞代碼是比代碼自己略高的設計層次,使用目標編程語言的元素會下降設計層次
- 在本意 intent 層面編寫僞代碼
- 在足夠低的層次編寫僞代碼,以即可以近乎轉化爲代碼
- 好的僞代碼能轉換爲註釋
- 僞代碼的好處:
- 僞代碼使得評審更容易
- 僞代碼支持反覆迭代精化思想:自頂向下,逐層拆解問題,解決問題
- 僞代碼使變動更加容易
- 僞代碼能使給代碼做註釋的工做量減小
- 僞代碼比其餘設計形式的文檔更容易維護
- 9.3 經過僞代碼編碼過程建立子程序
- 設計子程序 Design the Routine
- 檢查先決條件:子程序工做是否認義好,是否是與總體設計相匹配。是否項目必需的
- 定義子程序的解決的問題:
- 子程序要隱藏的信息
- 子程序的輸入
- 子程序的輸出
- 調用子程序前確保有關的前條件成立(輸入數據在特定範圍內,流已經初始化等)
- 在子程序將控制權交回調用程序前,確保後條件成立(輸出數據在特定範圍內,流已經關閉)
- 爲子程序命名
- 決定如何測試子程序
- 在標準庫搜索可用的功能:重用好的代碼,不重複造輪子
- 考慮錯誤處理
- 考慮效率問題:
第一種狀況絕大數系統而言,效率並非十分緊要。另外一種狀況是對少數系統而言性能很是重要。在除了上述兩種狀況,在子程序效率的優化是白費功夫的,由於主要的優化是在於完善高層的設計
* 研究算法和數據類型
* 編寫僞代碼:先寫頭註釋 head comment,再寫僞代碼
* 考慮數據
* 檢查僞代碼:確認很容易,很天然地理解子程序作些什麼以及怎樣作
* 在僞代碼中試驗一些想法,留下最好的想法(迭代):僞代碼試驗想法成本比代碼低。
> 用僞代碼反覆描述這個子程序,直到僞代碼寫出句子已經足夠簡單,你能夠把僞代碼直接變成代碼文檔爲止。最初僞代碼層次過高,不斷的精化和分解僞代碼,直到再寫僞代碼實在浪費時間爲止
* 編寫子程序代碼 Code the Routine
* 圖9.3 at p225
* 寫出子程序的聲明:把若是接口名稱起得直接了當,就不須要接口假定(interface assumption)的事情
* 把僞代碼轉變爲高層次的註釋
* 在每條註釋下填充代碼:
僞代碼至關於文章的提綱,每段僞代碼註釋描述類一段或者一句代碼
* 檢查代碼是否須要進一步分解
* 若是一行僞代碼下的代碼過多,能夠refactor重構成一個子程序
* 遞歸recursively地應用僞代碼編程過程。若是一行僞代碼下的代碼過
多,能夠把一行僞代碼拆分爲多行僞代碼
- 檢查代碼 Check the Code
- 在腦海檢查程序的錯誤:當子程序足夠短小精悍,檢查到程序全部可能的執行路徑、端點和異常狀況,不但要本身查(這叫桌面檢查 desk checking),能夠同行審查peer review,詳查walk-through或者審查inspection
從superstition迷信到理解,調查顯示只有5%的錯誤是因爲編譯器或者硬件形成的,因此遇到的錯誤大部分都是程序員自身形成的
* 編譯子程序:把編譯器的警告級別調到最高,使用像lint的檢查工具,及時消除產生錯誤信息和警告的全部根源
* 在調試器中逐行運行代碼
* 測試代碼
* 消除程序中的錯誤
- 收尾工做 Clean Up LeftOvers
- 檢查子程序的接口
- 檢查子總體的設計質量:內聚性;子程序間鬆散耦合;防護式編程C7
- 檢查子程序的變量C10-13
- 檢查子程序的語句和邏輯:有無泄漏資源,錯誤,死循環,錯誤嵌套C14-19
- 檢查子程序的佈局:格式化 C31
- 檢查子程序的文檔
- 除去冗餘的註釋
- 9.4 僞代碼編程過程的替代方案 Alternative to the PPP
- 測試先行開發(測試驅動開發)Test-first Development:在任何代碼以前先要寫出測試用例,使得程序可測試
- 重構refactoring C24
- 契約式設計 design by contract 即每一段程序都具備前條件preconditions和後條件postconditions
- 東拼西湊hacking
- 覈對表 P233
第三部分 變量 Variable
- 第十章 使用變量的通常事項 General Issue in Using Variables
- 10.1 數據認知: 列舉的常見的數據類型
- 10.2 輕鬆掌握變量定義:
- 隱式聲明:避免隱式聲明,可能致使編譯器的初始值不符合編程的要求,應該關閉隱式聲明,並聲明所有變量;
- 10.3 變量初始化原則:
- 初始化錯誤:
- 從未對變量賦值
- 變量值已通過期
- 變量的一部分被賦值
- 避免初始化方法:
- 在聲明時初始化變量
- 在靠近變量第一次使用的地方初始化它
- ****理想狀態下****,在第一次使用的地方聲明並初始化變量
- 在可能的狀況下,使用final或者const
- 在計數累加器i j,再次使用時忘記重置是一個常見的錯誤
- 在類的構造函數中初始化該類數據成員
- 檢查變量是否須要從新初始化
- 一次性初始化具名常量,用可執行代碼初始化變量。
- 10.4 做用域 Scope
- 使變量引用局部化:跨度span:變量引用點之間的距離;應該把變量的引用點集中起來
、 * 儘量縮短存活時間:就是變量最初引用點到最後引用點之間的距離
- 減小做用域的通常原則:
- 在循環開始前初始化該循環裏使用的變量,而不是在該循環所屬的子程序開始處初始化這些變量
- 直到變量即將被使用的時候再爲其賦值
- 把相關的語句放在一塊兒
- 把相關的語句提出成單獨的子程序
- 開始時採用嚴格的做用域,而後根據須要擴展變量的做用域
> private -> protected -> default- >public
- 10.5 持續性:有可能數據發生了變化,引用了過時的變量致使錯誤
- 在子程序加入調試代碼或者斷言檢查關鍵數據的合理性
- 準備拋棄變量時給它設置不合理的值 我的認爲在at C++
- 編寫程序假定是沒有持續性的,但不適合c++或者java中的static數據
- 養成使用全部數據前聲明和初始化的習慣
- 10.6 綁定時間:綁定時間越早靈活性越差,其實跟上面的初始化變量的指導是同樣的
- 10.7 數據類型和控制結構之間的關係:
- 序列型數據翻譯爲程序中的順序語句
- 選擇型數據翻譯爲程序中的if else語句
- 迭代型數據翻譯爲for repeat while等循環語句
- 10.8 爲變量指定單一用途
- 第十一章 變量名的力量
- 11.1 選擇好變量名的注意事項
- 最終重要的命名事項:
- 名字要徹底、準確地表達該事物
- 容易閱讀、不包含晦澀的縮寫、同時無歧義
- 以問題爲導向:好的名字是表達」什麼「(what)而不是」如何「(how),即反映問題而不是解決方案。變量應直指問題的領域而非計算機世界
inputRecord 比 employeeData
* 最適當的名字長度 Optimum Name Length:
* 有研究是平均長度在10到16個字符
* 有研究是平均長度在8到20個字符
* 最重要是強調當本身代碼中出現不少更短的名字,認真檢查確保名字含義足夠清晰
* 變量名對做用域的影響 The Effect of Scope on Variable Names、
* 對位於全局命名空間中的名字加入限定詞
* 若是編程語言不支持命名空間,就在對應的子系統加入前綴
- 變量名中計算值限定空間 Computed-Value Qualifiers In Variable Names
- 把總額 sum total、平均數 average 、最大值Max、最小值Min、記錄Record、字符串String、Pointer指針加入名字的後面
revenueTotal revenueAverage更具對稱性,更容易維護
Num放在開始位置表明總數,放在結束位置表明一個下標
customerCount表明員工總數 customerIndex表明某個特定的員工
- 變量名中的對仗詞 Common Opposites In Variable Names
- locked/unlocked
- min/max
- next/previous
- old/new
- opened/closed
- visible/invisible
- source/target
- source/destination
- up/down
- 11.2 爲特定類型的數據命名 Naming Specific Types of Data
- 爲循環下標命名 Naming Loop Index:
- 通常狀況下使用i、j、k
- 多層循環嵌套的狀況或者循環長度超過一兩行代碼,應該給計數器賦予更長的名字以表達其含義
- 爲狀態變量命名 Naming Status Variables
- 爲狀態變量取一個比flag更好的名字
- 標記應該用枚舉類型、具名常量或者做爲具名常量的全局變量對其進行賦值,增長可讀性
- 爲臨時變量命名 Naming Temporary Variables:在使用temp等命名前最好思考是否有能表達其含義的名字
- 爲布爾變量命名 Naming Boolean Variables:
- 謹記典型的布爾變量命名:
- done 表示事情是否完成,未完成前是false,完成後是true
- error 表示有錯誤發生,錯誤發生前是false,錯誤發生後是true
- found 表示某個值已經找到,在未找到該值前是false,找到該值後是true
- success或者ok 表示一項操做是否成功,失敗是false,成功是true
- 爲布爾變量賦予隱含「真/假」含義的名字
- status不是一個好的命名
- isXXX優勢是:不能用於哪些模糊的名字。缺點是可讀性較差
isStatus無心義 isFound 比found可讀性差
* 使用確定布爾命名
- 爲枚舉變量命名 Naming Enumerated Types:
- 能夠經過使用組前綴爲明確表示該類型的成員都同屬於一個組
Color_XXX
* 對於枚舉的命名有不一樣的觀點:有大小寫混合Color_Blue
;與常量相似的大寫Color.BULE
* 在處理枚舉像類的編程語言裏,處理枚舉很像類,因此枚舉成員老是冠以枚舉名字前綴,就無需重複前綴了
- 爲常量命名 Naming Constans:應命名該常量表明的抽象事物而非數值
- 11.3 命名規則的力量 The Power of Naming Covenstions
- 爲何要有規則
- 要求你更多按規矩辦事,集中精力投入關注代碼更重要的特徵
- 有助於項目之間傳遞知識
- 有助於學習新項目
- 有助於減小名字增生 name Proliferation
- 彌補編程語言的不足
- 強調相關變量之間的關係:把相關變量設置相同前綴將它們關聯起來
- 什麼時候採用命名規則
- 多人開發
- 程序須要轉交別人
- 程序規模太大,沒法同時瞭解全局,必需分而治之
- 程序開發週期過長
- 一個項目存在一些不常見的術語,在編寫代碼中使用術語或者縮寫的時候
- 11.4 非正式命名規則 Informal Naming Conventions
- 與語言無關的命名規則指導原則
- 與語言相關的命名規則指導原則:有C,C++,只列舉Java
- i,j是整數下標
- 常量所有大寫並用下劃線分割
- 類名和接口每一個單詞首字母大寫
- 變量名和方法名第一個單詞首字母小寫,後續單詞首字母大寫
- 除用於所有大寫的名字外,不使用下劃線做爲名字中的分隔符
- 訪問器子程序使用get和set前綴
- 混合語言編程注意事項
- 命名規則示例
- 包含如下三類信息:
- 變量的內容(是什麼)
- 數據的種類(具名常量,簡單變量,用戶自定義類型或者類)
- 變量的做用域(局部,私用的,類的,包的或者所有的做用域)
- 類成員數據:mXXX,全局變量:gXXX
- 11.5 標準前綴 Standardized Prefixes:分用戶自定義類型(UDT)的縮寫和語義前綴
- 用戶類型縮寫 User-Defined Type Abbreviation:UDT縮寫能夠標識被命名對象或者變量的數據類型
- 語義前綴 Semantic Prefixes:描述變量或者對象是如何被使用的
- c:count 數量
- first:數組須要處理的第一個元素
- g:全局變量
- i:數組的下標
- last:數組中須要處理的最後一個元素
- lim:數組中須要處理的元素上限,是非法的,不存在的上限,而last是合法的
- m:類一級的變量
- min:數組或者其餘種類列表中絕對最前一個元素
- max:數組或者其餘種類列表中絕對最後一個元素
- p:pointer 指針
- 標準前綴的優勢:使名字更緊湊、增長可讀性
- 11.6 建立具有可讀性的短名字 Creating Short Names That Are Readable
- 縮寫的通常指導原則:
- 使用標準縮寫(參考詞典)
- 去掉全部非前置元音???
computer->cmptr screen->scrn
* 去掉虛詞
> and、or、the等
* 使用每一個單詞的第一個或者前幾個字母
* 統一地使用單詞的第1、第二或者第三(自行肯定)字母后截斷
* 保留每一個單詞的第一和最後一個字母
* 使用名字中每一個重要單詞,最多不超過3個
* 去除無用的後綴
> ed,ing
* 確保不要改變變量的含義
- 11.7 應該避免的名字 Kind of Names To Avoid
- 避免使用使人誤解的名字或者縮寫
- 避免使用具備類似含義的名字
- 避免使用具備不一樣含義可是類似名字的變量:
通常是縮寫很類似的狀況如 clientReq 和 clientRes
* 避免使用發音類似的名字
* 避免在名字中使用數字
* 避免拼錯單詞
* 避免使用容易拼錯的單詞
* 不要僅靠大小寫來區分變量名
* 避免使用多種天然語言:只有英語,不使用漢語等
* 避免使用標準類型、變量和子程序名字:編程語言的關鍵詞
* 不要使用與變量含義無關的名字
* 避免在名字中使用容易混淆的字符:數字1對於字母l或者字母i,數字2對應字母z,數字5對應字母s,數字6對應字母g,數字0對應字母o
- 第十二章 基本數據類型
- 12.1 數值概論
- 避免神祕數值
- 可使用硬編碼的1或者0
- 預防除零錯誤
- 使類型轉換變得明顯:不一樣的數據類型之間會發生轉換時,利用顯式轉換而非隱式轉換
- 避免混合類型的比較:應該轉換成相同類型再進行比較
- 注意編譯器警告
- 12.2 整數 Integers
- 檢查整數除法
- 檢查整數溢出:在整數加法或者乘法的過程當中,留心較大的整數。
- 12.3 浮點數 Floating-Point Numbers
- 避免數量級相差巨大之間加減運算:解決方案:對於一系列相差巨大的數進行運算,先進行從小到大排序,從最小值開始把它們加起來,並不能消除舍入問題,可是能減小到最低限度。
1000 000.00+0.1可能等於1000 000.00
* 避免等量判斷:肯定數值在可接受的精度範圍內
> double類型變量,for循環中 加0.1,十次後不必定等於1.0
* 處理舍入偏差問題:
* 換用精度更高的變量類型
* 把浮點變量變成整數變量
* 檢查語言和函數庫對特定數據類型的支持
* 12.4 字符和字符串 Characters and Strings
* 避免神祕字符和神祕字符串
* 瞭解你的語言和開發環境是如何支持Unicode
* 在程序生命週期中儘早決定國際化/本地化策略
* 只支持一種文字語言,考慮使用ISO-8859字符集
* 須要支持多語言,請使用unicode
* 使用某種一致的字符串轉換策略
* 12.5 布爾變量
* 用布爾變量對程序加以文檔說明:對於表達式的結果賦予一個布爾變量,以提升可讀性
* 用布爾變量簡化複雜判斷:這是在上一條意見演化出來的,也是提升可讀性
* 若是須要的話,建立你本身的布爾類型
* 12.6 枚舉類型 Enumerated Types
* 用枚舉提升代碼可讀性
* 用枚舉提升代碼可靠性
* 用枚舉是程序易於簡化修改
* 使用枚舉做爲布爾值的替代方案:有多種失敗的類型
* 檢查非法數值
* 定義枚舉的第一項和最後一項以用於循環邊界
> Enum Country{
Country_First = 0;
Country_China = 0;
Country_USA = 1;
Country_Last = 1;
}
* 把枚舉的第一個元素留做非法值
> Enum Country{
Country_InvalidFirst = 0;
Country_First = 1;
Country_China = 1;
Country_USA = 2;
Country_Last = 2;
}
* 明肯定義項目代碼編寫標準中第一個和最後一個元素的使用規則
* 警戒給枚舉元素明確賦值而帶來錯誤:
> Enum Country{
Country_InvalidFirst = 0;
Country_First = 2;
Country_China = 2;
Country_UK = 4;
Country_USA = 6;
Country_Last = 6;
}
遍歷的時候會遍歷到1,3,5這些非法值
* 若是你的語言沒有枚舉類型:p307
* 12.7 具名常量 Named Constants
* 12.8 數組 Arrays
* 確保數組下標沒有越界
* 考慮用容器取代數組,或者將數組做爲順序化結構來處理
* 檢查數組的邊界
* 數組是多維,保證下標的使用順序正確,防止下標串話
* 在C中結合ARRAY_LENGTH()宏來使用數組
* 12.9 創造本身的類型(類型別名)Creating Your Own Types(Type Aliasing)
- 第十三章 不常見的數據類型
- 13.1 結構體 Structure:指使用其餘類型建立的數據,相似java中沒有公用子程序,徹底由公用數據成員組成的類,我的認爲就是封裝
- 用結構體明確數據關係:歸爲一類,關聯起來
- 用結構體簡化對數據塊的操做
- 用結構體簡化參數列表
- 用結構體減小維護
- 13.2 指針 Pointers???未學,略
- 用來理解指針的範例:指針:內存中的某個位置+如何解釋該位置的內容
- 內存中的位置:就是一個地址,以16進制數表示
- 如何解釋指針所指的內容:由指針的基類型 base type決定
- 使用指針的通常技巧:略
- 13.3 全局數據 Global Data
- 與全局變量有關的常見問題:
- 無心間修改了全局數據
- 與全局數據有關的奇異的和使人激動的別名問題:就是出現兩個或者以上的名字都是指同一個變量
- 與全局數據有關的代碼重入(re-entrant)問題:多線程狀況下全局數據不只是不一樣子程序共享,同時同一程序的不一樣拷貝之間也共享
- 全局數據阻礙代碼重用
子程序用到全局數據,不能直接將子程序複製到其餘地方(其餘類)裏面,解決方法:上策是修改舊類將全局數據局部化;下策是在新類建立與舊類相同的全局數據,致使像病毒同樣傳染
* 與全局數據有關的非肯定的初始化順序事宜
> 在初始化一個類的變量時須要使用其餘文件的初始化全局變量,因此須要採用明確手段保證兩個變量按照正確順序進行,否則將致使錯誤
* 全局數據破壞了模塊化和智力上的可管理性
* 使用全局數據的理由:
* 保存全局數據:好比程序是否debug等
* 模擬具名常量
* 模擬枚舉類型
* 簡化對極其經常使用數據的使用
* 消除流浪數據(tramp data):
有時候傳遞數據給一個子程序或者類,只是想傳遞給另外一個子程序或者類,若是調用鏈中間的子程序並不適用這一對象的時候,就稱這些數據爲流浪數據
* 只有萬不得已才使用全局數據
* 按照"局部數據->private數據->protected數據->全局數據"順序設置數據的做用域
* 區分全局變量和類變量
* 使用訪問器子程序
* 用訪問器子程序來取代全局數據
* 訪問器子程序的優點
* 得到對數據的集中控制:若是要修改結構方法是須要修改子程序便可
* 確保變量的全部引用獲得保護,避免出現異常
* 訪問器子程序能夠容易轉變爲抽象數據類型:即經過子程序名稱實現抽象,提升代碼可讀性
* 如何使用訪問器子程序
* 要求全部數據經過子程序訪問
* 不要把全局數據放在一塊兒,而是放在相應抽象水平的類裏面
* 用鎖來控制對全局數據的訪問:在多線程下,子程序訪問器加鎖,保證數據正確性
* 使得對一項數據的全部訪問都發生在同一抽象層上
> 若是有add(event),就會有remove(event)
* 如何下降使用全局數據的風險
* 建立一種命名規則來突出全局變量
> gXXX
* 爲全局變量建立一份註釋良好的清單
* 不要用全局變量存儲中間結果
* 不要把全局變量都放在一個大對象中並處處傳遞,以說明你沒有使用全局變量
> 全局變量應根據其抽象層次防到相應的類中
第三部分 語句 statement
- 第十四章 組織直線型代碼 Organizing Straight-Line Code
- 14.1 必須有明確順序的語句 statements That Must be in Specific Order
-
設法組織代碼,讓依賴關係變得很是明顯
-
使子程序名能突顯依賴關係
-
利用子程序參數明確顯示依賴關係
參數
init(expenseData);
dayExpensse(expenseData);
monthlyExpensse(expenseData);
anunalExpensse(expenseData);
帶返回值
expenseData = init(expenseData);
expenseData = dayExpensse(expenseData);
expenseData = monthlyExpensse(expenseData);
expenseData = anunalExpensse(expenseData);
用數據代表依賴關係不重要
init(expenseData);
dayExpenseData = dayExpensse(expenseData);
monthlyExpenseData = monthlyExpensse(expenseData);
anunalExpensseData = anunalExpensse(dayExpenseData ,monthlyExpenseData );
* 用註釋對不清晰的依賴關係進行說明
* 用斷言或者錯誤處理代碼來檢查依賴關係:可是增長類複雜度,採用的時候須要衡量利弊
- 14.2 順序無關的 語句 Statements Whose Order Don't Matter
- 使代碼易於自上而下地閱讀 Making Code Read From Top to Bottom:跟把相關代碼組織在一塊兒時道理是同樣的
- 把相關代碼組織在一塊兒 Grouping Related Statements
- 第十五章 使用條件語句 Using Conditionals
- 15.1 if語句
- 簡單的if-then語句
- 首先寫正確代碼路徑,再處理不常見狀況
- 確保等量分支是正確的:不要漏掉特定狀況
- 把正常狀況的處理放在if後面而不要放在else後面
- if後面不要跟空語句:要不就改爲 if(!XXX){};
- 考慮else語句:若是須要能夠配個空的else語句並加以說明
- 測試else語句的正確性
- 檢查if else語句是否是弄反
- if-then-else語句串 Chains of if-then-else statements:
- 使用布爾值調用簡化複雜的檢測
- 把最正確的狀況放在最前面
if(xxx){
}else if(xxx){
}
else if(xxx){
}
* 若是語言支持把if-then-else語句串替換成其餘結構:case語句,更清晰
- 15.2 case語句 case Statements
- 爲case語句選擇最有效的排序
- 按字母順序或者數字順序排列各類狀況:全部狀況的重要性相同
- 把正常的狀況放在前面
- 按執行頻率排列case語句
- 使用case語句的訣竅
- 簡化每種狀況對應的操做:對於某種狀況的操做過於複雜,應該變成一個子程序
- 不要爲了使用case語句而刻意製造一個變量
- 把default語句只用於檢查真正默認的狀況
還剩一個狀況,用default去檢查是不對的
* 使用case穿越(穿透)須要註釋說明狀況
* 覈對表at p635
- 第十六章 控制循環
- 16.1 選擇循環的種類 Selecting the Kind of Loop p367
- 種類:
- 計數循環 counted loop
- 連續求值循環 continuously evaluated loop
- 無限循環 endless loop
- 迭代器循環 iterator loop
對於C、C++、Java:for、foreach 、while、檢查位置都是開始 do- while是結尾
靈活度除了foreach 是嚴格以外其餘均是靈活
do-while至少執行一次,其餘能夠不執行
* 何時使用while循環 When to Use a Loop-While-Exit Loop
* 何時用帶退出的循環
* 正常帶退出的循環
* 帶退出的循環更容易理解
* 帶退出的循環可能使退出的地方不少,可能致使在調試、修改或者測試時被忽略,若是可能儘量把退出的代碼寫在一個地方
* 非正常帶退出的循環
* 何時使用for循環 When to Use a for Loop:
你在循環頭處寫好後即把它忘掉,無須再循環中作任何事情去控制它,若是有一個必須使循環從循環退出的條件,就使用while循環
* 何時使用foreach循環 When to Use a foreach Loop:消除循環內務處理算數,防止off-by-one越界錯誤
- 16.2 循環控制 Controlling the Loop p373
- 防止出現錯誤的方法:
- 減小能影響該循環各類緣由的因素:說了跟沒有同樣——
- 把循環內部當作子程序,把控制儘量放在循環體外
while(XXX && XXX && (XXX||XXX)){
XXXXXXXXXX
}
- 進入循環 Entering Loop
- 只從一個位置進入循環
- 把初始化代碼緊放在循環前面
- 用while(true)表明無限循環
- 在適當狀況下多使用for循環
由於for循環把循環控制代碼集中了,while須要在循環頂部初始化循環條件,而後在底部修改循環的相關代碼
* 在while循環更適用的時候,不要使用for循環
> 不是for(xx;xx;xx)中間代碼不是簡單對計數值進行判斷,而是其餘表達式則應當該爲while循環
* 處理好循環體 Processing The Middle of the Loop
* 用{}
將循環體重的語句包起來:我的:即便是一條語句也須要,由於擴展、修改程序可能會出現意料以外的錯誤
* 避免空循環:不要出現循環體爲空的狀況,應改爲do-while
* 把循環體的內務操做要麼放在循環開始處,要麼放在結尾處:內務操做如i= i+1這樣的表達式
* 一個循環只作一件事
* 退出循環 Exiting Loop
* 設法確認循環可以終止:考慮正常狀況、端點以及每一種異常狀況
* 不要爲了終止循環混亂修改for循環下標
* 避免出現依賴於下標最終取值的代碼:下標值只在循環體內有效,能夠說是縮小做用域的一種作法
* 考慮使用安全計數器
* 提早退出循環
* 考慮在while循環中使用break而不是布爾標記:
就是循環體中先後操做有條件限制關係,當達到某個條件,不執行後續操做時,應當使用break直接退出
* 當心那些有不少break散佈在循環中
* 在循環開始處使用continue:提升可讀性
* 若是語言支持,請使用帶標記號break結構:是break退出的目標一目標然
* 使用break和continue要當心謹慎
* 檢查端點 Checking EndPoints
> 簡單的循環:開始狀況+任意選擇的中間狀況+最終狀況,先腦海模擬,若是有複雜計算,手動檢查計算是否正確
* 使用循環變量 Using Loop Variables
* 用整數或者枚舉類型表示數組和循環的邊界
* 嵌套循環中使用有意義的變量名提升其可讀性
* 用有意義的名字防止循環下標串話
* 把循環下標變量限制在本循環內
* 循環應該多長 How Long Should a Loop Be
* 把循環代碼的行數限制在50行之內
* 把嵌套限制在3層之內
* 把長循環的內容移到子程序內
* 要讓長循環格外清晰
- 16.3 輕鬆建立循環-由內而外 Creating Loop Easily- From the inside Out p385
- 16.4 循環和數組的關係 Corresponse Between Loop And Arrays p387
- 第十七章 不常見的控制結構 Unusual Control Structures
- 17.1 子程序中多處返回 Multiple Return From a Routine p391
- 若是能增長可讀性,就用return
- 用防衛句子(guard clause)(早返回或早退出) 來簡化複雜的錯誤處理
- 17.2 遞歸 Recursion p393
- 使用遞歸的技巧
- 確認遞歸可以終止
- 使用安全計數器防止無限循環
- 把遞歸限制在一個子程序裏面
- 留心棧空間:防止棧溢出
- 不要用遞歸去計算階乘和斐波那契數列
- 17.3 goto p398
- 反對goto的觀點 The Arguments Against gotos
- 支持goto的觀點 The Arguments for gotos
- 關於goto的虛假辯論 The phony goto Debate
- 錯誤處理和goto Erro Processing And goto
- goto和在else字句中的共享代碼 goto And The Sharing Code In an else Clause
- goto使用原則總結 Summary of Guidlines For Using *gotos *
- 17.4 針對不常見控制結構的觀點 Perspective on Unusual Control Structures
- 第十八章 表驅動法 Table-Driven Methods
- 第十九章 通常控制問題 General Control Issue
- 19.1 布爾表達式 Boolean Expressions p431
- 用true或者false作布爾判斷 Using true of false For Boolean Tests
- 用true或者false作判斷,而不用0和1等數值作判斷
- 隱式地比較布爾值與true和false:
while(success){xx}
而不是while(success == true){xxx}
* 簡化複雜的表達式 Making Complicated Expression Simple
* 拆分複雜判斷並引入新的布爾變量
* 把複雜的表達式作成布爾函數
* 用決策表替代複雜的條件
* 編寫確定形式的布爾表達式 Forming Boolean Expression Positively
* 先確定再否認語句
> if(xx){xxx}else{xxx}
而不是if(!xx){xxx}else{xxx}
* 用狄摩根定理(逆反定理)簡化否認的布爾判斷
* not A and not B = not(A or B)
not A or not B = not (A and B)
* 用括號使布爾表達式更清晰 Using Parentheses to Clarify Boolean Expressions
* 用簡單的技術技巧來使括號對稱:開始爲0,遇到一個左括號加1,遇到一個右括號減1
* 把布爾值括在括號裏面
* 理解布爾表達式如何求值 Knowing How Boolean Expression Are Evaluated
* 按照數軸順序編寫數值表達式 Writing Numeric Expressions in Number-Line Order
* 從左到右,從大到小
* 與0比較的知道原則 Guidelines for Comparisons to 0
* 隱式地比較邏輯變量
* 把數和()相比較
* 在C中顯式地比較字符和零終止符(‘\0’)
* 把指針與NULL相比較
* 布爾表達式的常見問題
* java中 == 和equal的區別
- 19.2 複合語句(語句塊)Compound Statements (Blocks) p443
*把括號對一塊兒寫出
- 19.3 空語句 Null Statements p444
- 當心使用空語句
- 爲空語句建立一個DoNothing()預處理函數或者內聯函數:強調空語句
- 考慮若是換用一個非空的循環體,是否會讓代碼更清晰
- 19.4 馴服危險的深層嵌套 Taming Dangerously Deep Nesting p445
- 經過重複檢測條件中某一部分來簡化嵌套的if語句
- 用break來簡化嵌套if
- 把嵌套if轉換成一組if-then-else語句:即轉換成
if(xxx){xxx}elseif(xxx){xxx}elseif(xxx){xxxx}
- 把嵌套if轉換成case語句
- 把深層嵌套的代碼抽取出來放進單獨的子程序
- 使用一種更面向對象的方法:即多態
- 從新設計深層嵌套代碼
- 爭議 用狀態變量重寫代碼(增長複雜度)17.3
- 爭議 用防衛字句退出子程序,從而使代碼的主要路徑更爲清晰 17.1
- 使用異常 8.4
- 19.5 編程基礎:結構化編程 p454
- 核心思想:應用程序只採用一些單入口,單出口的控制結構。單入單出的控制結構就是一個代碼塊,只能從一個位置開始執行,而且只能結束於某個位置。(有且只有一個入口和出口)
- 結構化編程的三個組成部分 The Three Components of Structured Programming
- 順序 Sequence:賦值和調用子程序
- 選擇 Selection:if和case語句
- 迭代 Iteration:
- 19.6 控制結構與複雜度 Control Structure and Complexity p456
- 複雜度的重要性 How Import is Complexity
- 下降複雜度的通常性原則 General Guidelines For Reducing Complexity
程序一開始爲1
一旦遇到關鍵詞或者其同類加1:if repeat,while,for,and,or
給case語句中每一種狀況加1
* 如何處理複雜度的度量結果
> 0-5子程序可能不錯|
6-10得想辦法 簡化子程序類
10+把子程序 的某一部分拆分紅另外一個子程序並調用它
第五部分 代碼改善 Code Improvement
- 第二十章 軟件質量描述 p463
- 20.1 軟件質量的特性
- 外在特性:
- 正確性 Correctness 指系統規範、設計和實現方面的錯誤的稀少程度
- 可用性 Usability:指用戶學習和使用的成本
- 效率 Efficiency:指佔用的內存,儲存和執行時間
- 可靠性 Reliability:在指定必需條件下,完成所需功能的能力:很長的無端障時間
- 健壯性 Robustness:接受無效數據或者在壓力環境下運行的能力
- 完整性 Integrity:阻止對程序或者數據進行未經驗證或者不正確訪問的能力
- 適應性 Adaptability:爲特定應用或者環境參數設計的系統,能不修改的狀況給其餘應用或者環境使用
- 精確性 :輸出結果的偏差程度
- 內在特性:
- 靈活性 Flexibility: 適應需求
- 可維護性 Maintainability
- 可移植性 Portability
- 可重用性 ReuSability
- 可讀性 Readability
- 可測試性 Testability
- 可理解性 Understandability
- 20.2 改善軟件質量的技術
- 20.3 不一樣質量保障技術的相對效能
- 20.4 何時進行質量保證工做
- 20.5 軟件質量的廣泛原理
- 第二十一章 協同構建 p479
- 第二十二章 開發者測試 p499
- 22.1 開發者測試在軟件質量中的角色 p500
- 測試的種類:
- 單元測試(Unit testing):測試的代碼:一個程序員或者一個團隊編寫 代碼規模:完整的類、子程序或者小程序
- 組件測試(Component testing)測試代碼:一個類、包小程序或者其餘程序元素 代碼規模:涉及到多個程序員或者多個團隊
- 集成測試(Integration testing):是對兩個或更多的類、包、組件或者子系統進行聯合測試。這些組件由多個程序員或者開發團隊所建立。
- 測試的做用:
- 測試與其餘開發活動背道而馳,測試的目標識找出錯誤,成功的測試是弄垮軟件,而其餘開發活動是避免程序錯誤和軟件崩潰
- 測試永遠不可能完全證實程序中沒有錯誤
- 測試並不能解決錯誤,改善軟件質量
- 程序員傾向於作出」乾淨「的測試
- 構建中測試
- 構建期間:先根據需求構想子程序->編寫子程序或者類->先在腦海檢查->進行復查或者測試
- 每寫一個子程序,對其進行獨立測試,確實不容易,可是單獨調試比繼承以後再測試容易多。
- 新加入的子程序發現新的錯誤,就知道這是子程序或者其接口引起的問題
- 22.2 開發者測試的推薦方法 Recommend Approad to Developer Testing p503
- 基礎方法:
- 對每一項相關的需求進行測試
- 對每隔相關的設計關注點進行測試
- 用基礎測試basic testing來擴充針對需求和設計的詳細測試用例:增長數據流測試 data-flow test,而後補充其餘所需的測試用例
- 使用一個檢查表,其中記錄着你在項目中所犯的錯誤
- 測試先行仍是測試後行:測試先行
- 先寫測試用例只是工做順序問題:所花代價同樣
- 先編寫測試用例,能夠更早發現缺陷
- 開發者測試的侷限性
- 開發者測試傾向於」乾淨「測試
- 開發者測試對於代碼覆蓋率過於樂觀
- 開發者測試每每忽略一些更復雜的測試覆蓋率類型
- 22.3 測試技巧錦囊 p505
- 不完整的測試Incomplete Testing:進行完整測試是不可能的事情
- 結構化的基礎測試 Structured Basic Testing
- 測試程序中的每一條語句至少一次
- 結構化基礎測試最少數量計算:
- 對於經過子程序的直路,開始的時候加1
- 遇到每一個關鍵詞或者等價物加1,如:if、while、repeat、for、and以及or
- 遇到每一個case語句就加1
- 數據流測試 Data-Flow Testing:數據流出錯率不低於控制流
- 數據的狀態
- 已定義:數據已經初始化了,可是沒有被使用
- 已使用:數據已經用於計算或者做爲某子程序的參數
- 已銷燬:變量出了做用域或者指針已經被釋放
- 數據的狀態的組合:正確的是已定義-已使用
- 等價類劃分 Equipment Partitioning
- 猜想錯誤 Error Guessing
- 邊界值分析 Boundary Analysis
- 幾類壞數據 Classes of Bad Data
- 數據太少(沒有數據)
- 太多的數據
- 錯誤(無效)的數據
- 未初始化的數據
- 幾類好數據 Classes of Good Data
- 正常的狀況-中間或者指望值
- 最小的正常局面
- 最大的正常局面
- 與舊數據的兼容性
- 22.4 典型錯誤 p517
- 錯誤不是均勻分佈的
- 絕大數錯誤與少數幾個具備嚴重缺陷的子程序有關
- 錯誤有幾大類:結構方面的錯誤?,數據方面的錯誤,已實現的功能(多是說隨着迭代,新需求跟舊實現之間的錯誤)
- 大多數錯誤的影響範圍有限
- 大多數構建錯誤是編程人員的錯誤形成的
- 拼寫錯誤是一個常見的問題
- 22.5 測試支持工具 Test-Support Tools p523
- 爲測試各個類構造腳手架 Building Scaffolding to Test Individual Classes
- 啞類(模仿對象/樁對象) dummy class(mock object/stub object):根據所需的真實性決定他們與現實的近似程度
- 調用待測試的真實函數的僞造函數,稱爲「驅動函數」或者「測試夾具」
- 測試數據生成器 Test-Data Generations
- 產生程序員意想不到的數據組合
- 比起手工構造測試數據,隨機數據生成器可以更完全地進行測試
- 覆蓋率監視器 Coverage Monitors
- 數據記錄器/日誌記錄器
- 符號調試器
- 系統干擾器
- 錯誤數據庫
- 22.6 改善測試過程 Improving Your Testing p528
- 有計劃的測試
- 從新測試(迴歸測試):在完全檢查代碼的前提下,當相關代碼發生改變以後,須要從新測試,可能產品迭代增長新的測試用例的同時應保留舊的測試用例
- 自動化測試
- 22.7 保留測試記錄 Keeping Test Records p529
做者:白樺葉 連接:https://www.jianshu.com/p/7b7228ebba55 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。