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


