對整本書中提到的優化手段做了一系列總結
17.1 註釋
17.1.1 不恰當的信息
- 註釋只應該描述對應代碼和設計的技術信息,不要說廢話
17.1.2 廢棄的註釋
- 廢棄的註釋會遠離它們曾經描述的代碼,更有可能這些代碼早已經不存在了
17.1.3 冗餘註釋
- 註釋應該描述代碼本身無法描述清楚的信息,不要重複的描述
17.1.4 糟糕的註釋
- 如果要編寫一條註釋,就應該花時間保證寫出最好的註釋
17.1.5 註釋掉的代碼
- 如果你現在認爲這段代碼沒用了,請直接刪掉
- 版本管理工具會幫你記住這段代碼,所以完全沒必要讓它留下,這隻會帶來混淆
17.2 環境
17.2.1 需要多步才能實現的構建
- 構建系統應該是單步的小操作
- 應該能夠用單個命令導出系統,並用單個指令進行構建
17.2.2 需要多步才能做到的測試
- 應該能夠用單個操作就可以運行全部的單元測試
17.3 函數
17.3.1 過多的參數
- 函數的參數數量應該儘可能少
- 三個以上的參數應該儘可能避免
- 這個我目前還很難做到
17.3.2 輸出參數
- 輸出參數是一種違法直覺的行爲
- 參數應該只用來輸入,而不是輸出
- 如果函數一定要改變輸入參數的值,用來給後續步驟使用,那麼應該改爲用返回值
17.3.3 標識參數
- 一個函數應該只做一件事,而布爾值參數的傳入就表明這個函數做了不止一件事
17.3.4 死函數
- 永遠不被調用的方法就應該被丟棄
17.4 一般性問題
17.4.1 一個源文件中存在多種語言
- 理想的源文件應該只包括一種語言
17.4.2 明顯的行爲未被實現
- 如果明顯應該實現的行爲沒有被實現,那麼函數的名稱就相當於沒有真實表達代碼的含義
17.4.3 不正確的邊界行爲
- 不要依賴直覺,追蹤每種邊界條件,並編寫對應的測試
17.4.4 忽視安全
- 很多程序員習慣性的忽視 IDE 給出的 WARNING 警告,這非常危險,應該盡其所能消滅所有文件中的警告信息
17.4.5 重複
- 依據 DRY 原則( Don’t Repeat Yourself )
17.4.6 在錯誤的抽象層級上的代碼
- 將較低層級的概念放在派生類中,將較高層級的概念放在基類中
- 良好的軟件設計要求分離位於不同層級的概念,並將它們放在不同的容器中
17.4.7 基類依賴於派生類
- 基類需要依賴派生類,這顯然是本末倒置的
17.4.8 信息過多
- 設計良好的接口提供的函數不會需要依賴其他函數
17.4.9 死代碼
- 在設計改變時,死代碼不會隨之更新,雖然它還是能通過編寫,但這段代碼對程序本身已經沒有用處,應該毫不猶豫的刪除
17.4.10 垂直分隔
- 變量和函數定義的位置應該離它們被使用的位置儘可能的近一些
17.4.11 前後不一致
- 遵循 最小驚異原則 ,做到從一而終
17.4.12 混淆視聽
- 沒有用到的變量,從不調用的函數,沒有信息量的註釋,都應該被刪除
17.4.13 人爲耦合
- 不互相依賴的東西不該產生耦合
- 兩個沒有直接邏輯關聯的模塊不應該存在耦合
17.4.14 特性依戀
- 類的方法只應該對自身類中的變量和函數感興趣
17.4.15 選擇算子參數
- 使用多個函數,比向單個函數中傳遞布爾參數來選擇函數行爲要更清晰
17.4.16 晦澀的意圖
- 代碼要儘可能具有表達力
17.4.17 位置錯誤的權責
- 軟件開發者做出的最重要決定之一:這段代碼應該放在哪
17.4.18 不恰當的靜態方法
- 通常應該傾向於選用非靜態方法
- 如果的確要使用靜態方法,請確保這個方法後期不會有多態的需求
17.4.19 使用解釋性變量
- 只要把計算過程打散成一系列良好命名的中間值,就算是不透明的模塊也會變得透明
- 但在這之前需要考慮爲什麼這段計算過程會不透明
17.4.20 函數名稱應該表達其行爲
- 如果必須查看函數的實現才知道其用途,說明應該換一個更好的函數名
17.4.21 理解算法
- 編程是一種探險,在開始使用某個工具實現功能之前,先儘可能瞭解這個工具能夠做到的事情
- 不要盲目的用自己當前掌握的知識直接解決問題,請先閱讀文檔
- 通常工具中的實現會比你自己想的要更好
17.4.22 把邏輯依賴改爲物理依賴
- 如果某個模塊依賴於另一個模塊,這種依賴應該是物理的,而不是邏輯的
17.4.23 用多態替代 if / else 或 switch / case
- 對於給定的選擇類型,不應該有多個 switch 語句
- 如果有多個,請使用多態
17.4.24 遵循標準約定
- 每個團隊都應該遵循基於通用行業規範的編碼標準
17.4.25 用命名常量代替魔術數
- 魔術數不僅僅是說數字,這是泛指任何不能自我描述的符號
17.4.26 準確
- 表達意思含糊或不準確的代碼,要麼優化,要麼刪除
17.4.27 結構甚於約定
- 使用強制的結構太替換脆弱的命名約定
17.4.28 封裝條件
- 應該把條件判斷語句中的複雜表達式,抽離成方法
17.4.29 避免否定性條件
- 否定式比肯定式難理解,儘可能將條件表示爲肯定形式
17.4.30 函數只該做一件事
- 遵循單一權責原則
17.4.31 掩蔽時序耦合
- 譯者又開始文縐縐的讓我受不了了,什麼掩蔽?直接說隱蔽不好嗎???
- 如果代碼存在調用順序上的耦合,就應該明確表示,放着出現因爲調用順序出錯而導致的報錯
17.4.32 別隨意
- 構建代碼需要理由,而理由應該和代碼結構相吻合
17.4.33 封裝邊界條件
- 邊界條件難以追蹤,所以應該把處理邊界條件的代碼集中到一起,不要散落在代碼中
17.4.34 函數應該只在一個抽象層級上
- 當根據抽象界限對函數進行拆解時,經常會發現隱蔽在之前的結構中的新抽象界限
17.4.35 在較高層級放置可配置數據
- 可配置的數據通常具有通用性,放置在較高層級則可以保證其通用性
17.4.36 避免傳遞瀏覽
- 模塊本身不要了解太多協作者的信息,而應該讓協作者提供給模塊所需要的全部服務
17.5 Java
17.5.1 通過使用通配符避免過長的導入清單
- 現代 IDE ,例如 IDEA ,已經可以做到自動優化導入清單
17.5.2 不要繼承常量
- 別利用繼承來使用常量,例如將常量放置在基類中
- 應該直接使用常量類實現靜態導入
17.5.3 常量 vs 枚舉
- 如果你感覺某個地方需要一個常量,請使用枚舉
17.6 名稱
17.6.1 採用描述性名稱
- 事物的意義隨着軟件的演化而發生變化,所以要經常對變量、方法的名稱進行重新評估
17.6.2 名稱應與抽象層級相符
- 類和方法的名稱應該能體現當前的抽象層級
17.6.3 儘可能使用標準命名法
- 使用名稱基於現存的約定,會更容易理解
- 與項目直接相關的名稱越多,後來接收代碼的人就可以更快的理解
17.6.4 無歧義的名稱
- 選用不會混淆方法或變量的名稱
17.6.5 爲較大作用範圍選用較長名稱
- 名稱的長度應該和其作用範圍成正比
17.6.6 避免編碼
- 不應該在名稱中包含類型或作用範圍信息,例如 m_score
17.6.7 名稱應該說明副作用
- 名稱應該說明函數、變量或類的一切信息
17.7 測試
17.7.1 測試不足
- 一套測試應該測到所有可能失敗的情況
- 只要還有沒被測試過的條件,或是還有沒被驗證過的計算,就說明測試不足
17.7.2 使用覆蓋率工具
- 覆蓋率工具可以直觀的彙報測試不足的模塊、類和方法
17.7.3 別略過小測試
- 小測試易於編寫,其文檔上的價值高於編寫成本
17.7.4 被忽略的測試就是對不確定事物的疑問
- 如果因爲需求不明確而不能確定某個行爲細節,可以用註釋掉的測試來表達疑問
17.7.5 測試邊界條件
- 特別注意測試邊界條件,算法的中間部分正確,但邊界判斷錯誤的情況很常見
17.7.6 全面測試相近的缺陷
- 缺陷趨向於扎堆,在某個方法中發現一個缺陷時,最好全面測試那個方法
17.7.7 測試失敗的模式有啓發性
- 通過找到測試用例失敗的模式來診斷問題所在
17.7.8 測試覆蓋率的模式有啓發性
- 查看沒有被測試過的代碼,通常可以發現其他測試代碼失敗的線索
17.7.9 測試應該快速
- 慢速的測試是不會被運行的測試
17.8
- 專業性和技藝來自於驅動規程的價值觀