《代碼大全》讀書筆記

第一部分 打好基礎(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ù)成員
  • 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/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

    • 為常用操作確立命名規(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
  • 第八章 防御式編程 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
  • 第九章 偽代碼編程過程 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è)計->編寫子程序的代碼->檢查代碼
        • 復(fù)審并測試整個類 :在子程序創(chuàng)建的同時經(jīng)過測試,在整個類可以工作后,應(yīng)該再對整體進(jìn)行復(fù)查和測試,以便于發(fā)現(xiàn)在子程序獨立測試層次上無法發(fā)現(xiàn)的問題
    • 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)行代碼
        * 測試代碼
        * 消除程序中的錯誤

    • 收尾工作 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

第三部分 變量 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代表某個特定的員工

    • 變量名中的對仗詞 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
    • 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可讀性差
          * 使用肯定布爾命名

    • 為枚舉變量命名 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ù)語或者縮寫的時候
    • 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、orthe
          * 使用每個單詞的第一個或者前幾個字母
          * 統(tǒng)一地使用單詞的第一、第二或者第三(自行確定)字母后截斷
          * 保留每個單詞的第一和最后一個字母
          * 使用名字中每個重要單詞,最多不超過3個
          * 去除無用的后綴
          > ed,ing
          * 確保不要改變變量的含義

    • 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

  • 第十二章 基本數(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ù)據(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決定
      • 使用指針的一般技巧:略
    • 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ù)阻礙代碼重用

子程序用到全局?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
  • 第十五章 使用條件語句 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語句,更清晰

    • 15.2 case語句 case Statements
      • 為case語句選擇最有效的排序
        • 按字母順序或者數(shù)字順序排列各種情況:所有情況的重要性相同
        • 把正常的情況放在前面
        • 按執(zhí)行頻率排列case語句
      • 使用case語句的訣竅
        • 簡化每種情況對應(yīng)的操作:對于某種情況的操作過于復(fù)雜,應(yīng)該變成一個子程序
        • 不要為了使用case語句而刻意制造一個變量
        • 把default語句只用于檢查真正默認(rèn)的情況

          還剩一個情況,用default去檢查是不對的
          * 使用case穿越(穿透)需要注釋說明情況
          * 核對表at p635

  • 第十六章 控制循環(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)格之外其他均是靈活

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)記:

就是循環(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)控制語句
      *偽代碼->代碼
  • 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
  • 第十九章 一般控制問題 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ū)別

    • 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)用它

第五部分 代碼改善 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ì)量的普遍原理
  • 第二十一章 協(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)的組合:正確的是已定義-已使用
      • 等價類劃分 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ù)庫
    • 22.6 改善測試過程 Improving Your Testing p528
      • 有計劃的測試
      • 重新測試(回歸測試):在徹底檢查代碼的前提下,當(dāng)相關(guān)代碼發(fā)生改變之后,需要重新測試,可能產(chǎn)品迭代增加新的測試用例的同時應(yīng)保留舊的測試用例
      • 自動化測試
    • 22.7 保留測試記錄 Keeping Test Records p529
  • 第二十三章 調(diào)試 p535
  • 第二十四章 重構(gòu) p563
  • 第二十五章 代碼調(diào)整策略 p587
  • 第二十六章 代碼調(diào)整技術(shù) p609
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容