我的程式碼會說話

  1. 觀察問題 : 第一章.劣質程式碼帶來的劣質體驗
  2. 瞭解問題 : 第二章.劣質程式碼是怎麼產生的
  3. 研究問題 : 第三章.自我表達的程式碼
  4. 解決問題 : 第四章 ~ 要慢慢的讀,慢慢的瞭解了
1. 劣質程式碼帶來的劣質體驗
1.1 程式碼的"可讀性問題"
以下這些問題都會造成閱讀上的障礙及妨礙
  1. 命名類的問題
    1. 命名缺乏統一性
    2. 命名沒有考慮呼叫時的情形 : 例如 車.停車 car.parkingCar
    3. 語言命名 : 例如使用羅馬拚音命名
    4. 命名用詞不當 (應該是不瞭解英文所致)
    5. 超長的命名
    6. 命名含義模糊
    7. 命名和行為不一致
    8. 否定式命名 ***
    9. 無意義命名
    10. 序號式命名
    11. 工程名稱為類別名稱的首碼
    12. 超短命名
    13. 匈牙利命名法 : 例 strClassmateName
  2. 註解類的問題
    • 每步皆註解 (乾脆用中文寫程式就好了)
    • 錯誤的註解
    • 修改歷史記錄註解 (有版本控管之後就不必要了)
    • 長方法中的分段註解 (應該要致力於縮短方法......)
    • 複製名稱的註解
    • 複製說明文件的註解
    • 缺少註解 (try cache 忽略 exception的處理時要說明, 不然....)
    • 編輯器自動產生的註解 (產生的範本,但沒實際去調整及說明內容, 有產生跟沒產生是一樣的)
  3. 風格類的問題
    • 長方法
    • 長參數列表 (參數愈少愈好)
    • 長判定語句 (if x1 && x2 && x3 && x4......
    • 長分支
    • 魔法數字 (這個我不太懂?? 是指在程式中重覆重某個數字, 而不把它用特定的常數來表達嗎?)
    • 字串直接引用 (直接使用 carName == "AUDI" || carName == "裕隆")
    • 冗餘的常數定義
    • 意思不明的邏輯
    • 變數意思不穩定 (應該是命名的問題吧!而且也不要一個變數用在不同意思的區塊上)
    • 回傳值意思不穩定 (1-100 代表,...101代表...102代表...
    • 無用的方法或變數
    • 詭異程式碼 (b = (--c<<a>>3)*(a++)
  4. 結構類的問題
    • do-whild 禁用引起的重複
    • switch-case 引起的長分支
    • 莫名其妙的 default (switch 的)
    • 被忽略的 Exception (指try catch 沒處理)
    • 全域變數做為回傳值 (那就不用回傳了不是嗎?)
    • 不必要的 Guard 程式碼 (為了避免null或是其他狀況, 可以在建構式或工廠類別中建立時確保)
    • 巢狀過深
    • 輸出型參數 (所以建立少用輸出型參數)
    • 冗餘的臨時變數
    • 不合理的錯誤編號
  5. 架構類的問題
    • 闗係混亂
      • 類別的循環引用 (a用了b, 然後b也用了a)會有記憶體空耗而進一步的memory leak
      • 錯誤的繼承
      • 不當的從屬關係
      • 大雜燴類別 (例如一個類別負責UI又資料處理又邏輯處理之類的)
    • 重複與類似
    • 層層深入的private方法 (表示原來的code太長, 需要提取出來變成更多的private方法.)
    • 墨守成規
1.2 程式碼的可測試性問題
  1. 難以建構測試設備(環境)
  2. 難以拆分做單元測試
1.2 程式碼的可維護性問題
  1. 需求變更難以應對
    • 粘滯 (牽一髮而動全身)
    • 脆弱 (一旦修改就會破壞原有結構)
    • 僵硬 (程式碼間總是互相牽制,導致難以入手)
  2. 糾纏不清的Bug
    • 難以重現的Bug
    • 難以定位的Bug
    • 難以修改的Bug
2. 劣質程式碼是怎麼產生的
  1. 理論知識匱乏
    • Copy、Paste
    • 照流程圖來寫程式 (這個倒是沒經驗過)
    • 臨時對策 (這會有技術債務)
    • 定式思維
      • 認為動作必須是方法 (也可以用介面來處理....)
      • 認為狀態只能採用整數來定義 (範例也是使用介面來處理)
      • 認為switch一定要有default
      • 認為Listener必須寫成匿名類別
      • 認為for必須有下標 (用for each)
  2. 對程式語言不熟悉
    • 泛型
    • Regular Express (正規表示式)
    • List 排序
    • 列舉型別
    • toString
    • clone()
  3. 對開發環境不熟悉
  4. 對設計方法不瞭解
  5. 程式設計的習慣不佳
    • 缺少 "必要" 的註解
    • 迷信 IDE 自動產生的程式碼
    • 命名習慣不好
    • 沒有管理好程式碼的職責
    • ...
  6. 英語能力不足 (涉及精確及說故事的本領)
  7. 管理人員的誤導 (coding分組導致程式碼、編碼規範的老舊或不合時宜、時間壓力急促成事、限制開發人員不能怎樣怎樣...)
3. 自我表達的程式碼 這是本書的核心概念,就是你寫的程式碼要會表達、能說故事、精簡易懂 (有時牽涉到開發者的程度、所以不要過分 design)
4. 理論知識的補充
物件導向的基礎知識
  • 封裝 : 把物件的資料和行為封裝在一起,外部透過抽象的介面存取物件,進而達到隱藏物件細節的目的。
    • 介面、抽象類別 : 介面沒有成員變數、介面只能有抽象方法。但抽象類別可以有建構子、具體實作的方法,介面則沒有。類別可以實作多個介面,但只能繼承一個抽象方法
  • 繼承
  • 多型
    • override
    • overload
設計的基本原則
  • 單一職責原則
  • 開放封閉原則
  • Liskovq替換原則
  • 介面隔離原則:一個類別對另外一個類別的依賴,應該取決於最小介面,應該將不同職責的任務分成若干個小的介面,客戶端不應該依賴於它們用不到的方法。
  • 依賴倒置原則:上層不應該依賴於下層,二者都該依賴於介面,抽象不依賴於實作,實作應該依賴於抽象。
  • 德摩特爾法則:最小知識原則,即一個物件對另一個物件的內部瞭解得越少越好。A、B、C...類別A會呼叫B , 但類別A不要透過類別B來呼叫C的方法...
  • 不要自我重複 (DRY)
圈複雜度
是就是要減少巢狀、廻圈......
5. 熟悉程式設計的環境
講的是關發環境的自動化功能,VS大部份都有預設值,只針對不熟的部份說明。
儲存動作與自動格式化
快速修復
重構
在不改變程式碼功能的前提下,透過調整程式碼的結構來提高程式碼的可讀性、可測試性和可擴展性的一種手法。
  • 修改名稱 (變數、方法、類別......等)
  • 提取方法 , 將長的程式碼選起來後,直接建立成另一段方法來達到程式碼的可讀性。
6. 程式語言的學習
  1. 註記 (屬性) [Attribute]
    • 利用註記來簡化的驗證模型
    • 利用註記來簡化的權限驗證模型
  2. 反射 (reflection)
  3. 例外 (exception)
    1. checked exception 跟 unchecked exception
    2. exception 可以繼承再改為自己所需要的
    3. 用 exception 代替錯誤碼 (也就是不要發生錯誤時回傳訊息, 應該發生錯誤時 throw exception , 正常時繼續走, 用try cache 包起來)
    4. DAL 層拋出 exception 時應該包起來為一個自訂訊息,直接丟給前端很難讓使用者理解。
    5. 不要 catch 基本的 Exception
    6. 多重 cache
    7. finally
  4. 泛型 ()
7. 設計方法的學習
  1. Design Patterns 設計模式 連結
  2. Dependency Injection 相依性注入 連結
  3. Map
  4. 採用位元遮罩來減少類別的個數 (位元遮罩的意思是將一個數字的每一位元都常作一個開關的旗標。尚把若干個位元放在一起時,能夠表現的狀態數就多達2的n次方。)
    • 位元遮罩
    • 取代多型
    • 替代列舉
  5. List 處理 Z-Order