這是一本被前輩稱贊, 另一個馬丁的知名著作, 被賦予學習如何寫出漂亮代碼的教皇級手冊, 最近(再)瀏覽, 有諸多感受.
總結一下就是如果你是新手, 可能看優秀的open source學習更好; 如果你是老手, 可能他說的東西要么你會覺得太啰嗦, 要么你會覺得例子太細節, 太教條, 無法舉一反三.
不過一些General的要點思想我摘錄了出來, 有些還包括個人的觀點, 記錄在此, 以備查閱.
- 勒布朗(LeBlanc)法則, Later equals never
- 代碼如果爛了就會越來越爛, 所謂破窗原理, 以前老是也提過叫代碼腐化
- 命名
- 命名應該合理, 成員變數不用加前綴, 靠高亮就很好
- 介面加前綴I也不太好, 不如實作加Imp
- 方法命名最好使用動詞或動詞短語
- 多使用計算機領域的詞匯
- 有時候命名在語境里才有意義, 有時候變數命名也不需要重復語境
- 函式
- 函式短小一些比較易懂, 20行內最佳
- if/else/while代碼塊封裝函式, 只有一行最好 (有點激進?)
- 一個函式只干一件事情
- 復雜的switch試圖用多型取代, 然后封裝在抽象工廠里
- 同一型別函式命名風格應該一致, 使用描述性的陳述句描述比難懂的短詞更好
- 一元函式最普遍, 標識函式(引數為boolean)的不如分成兩個函式, 二元函式盡可能轉換成一元函式
- 引數太多可能需要封裝類了
- 好名字的函式一般是動詞, 或者動詞+關鍵字
- 函式一般要么做事, 要么回答事, 不可兼得 (其實也不一定, 比如很多回傳boolean狀態的函式)
- 推薦使用例外代替錯誤代碼
- 最好將try/catch單獨抽入一個函式 (如Android代碼會對RemoteService呼叫進行類似封裝, 吃掉exception)
- 使用列舉表示錯誤碼會讓改動變得繁瑣 (重新匯入或者編譯部署), 大家都依賴這個列舉, 應該用例外與繼承取代
- 不要重復, 偶爾可以考慮AOP這類的方法解決
- 注釋
- 注釋少要比注釋多有用, 注釋多說明代碼糟糕 (其實分情況吧)
- 有目的性的注釋還是有用的, 比如闡述, 放大, 警告, 定期維護的TODO等
- 如果代碼不用, 不要注釋掉, 直接刪掉
- 格式
- 橫向, 豎向對齊, 間隔, 縮進等. 其實目前formater已經很強大了, 養成良好的format習慣, 漸漸的寫出來的代碼就會像直接format后一樣
- 物件與資料結構
- 面向程序與面向物件對立, 前者容易加函式, 不容易加物件, 后者容易加物件, 而不容易加函式
- 理想情況下, Law of Demeter認為, 類不應該操作物件內部的東西, 如不應該操作函式回傳物件的方法. 目的只是為了降低復雜度, 認為這樣將私有變數公開化, 增加了重構的難度, 如添加新函式, 方法等, 將資料結構與物件邏輯耦合在一起.
- 針對于上面的問題, 原則應該是物件暴露行為, 隱藏資料, 資料結構暴露資料
- 錯誤處理
- Java特色的受控例外(Checked Exception), 必須得被catch, 對受控例外的修改會引起上層所有呼叫方法的改動, 盡量不要使用, 其他語言只有RuntimeException
- 可以包裝第三方邏輯, 封裝自己的例外類, 簡化為只catch一種例外
- 簡化使用也可以將例外處理完全封裝進去, 回傳特例即可
- 輕易不要回傳null, 傳入null
- 代碼的堅固與干凈不沖突, 所以添加一些check, throw相應的Exception也合理
- 邊界
- 所謂邊界就是自己可以控制到的程式與第三方的邊界, 通常需要通過封裝的辦法來劃清邊界, 限制那些無法控制的第三方, 如直接Wrapper或者Adapter模式等
- 單元測驗
- 測驗需要整潔, 需要跟隨代碼一起更新, 測驗的最大用途是保證你后續的修改有信心
- 測驗的整潔主要講的是可讀性, 即分為三個環節, build->operate->check, 可以把繁雜的準備封裝起來
- 測驗API是漸漸重構演進過來的, 也不可能是起初就設計出來
- 有人建議每個測驗一個assert, 但是這樣會有很多重復的代碼, 不過可以利用
Template Method來解決. 不過也不一定必須一個, 做到最小化就可以了 - 整潔還有五條規則, Fast, Independent, Repeatable, Self-Validation, Timely
- 這里講到TDD的好處, 主要是幫助你覆寫更多的測驗, 如果寫完再測, 發現測不了, 就不寫了.
- 類
- 類應該短小, 權責應該足夠單一, 內聚性應該高
- 如果期望抽走一部分邏輯, 最好連相關函式引數也抽走
- 簡化類的程序應該小步, 每一步都運行測驗一下
- 類的精簡是為了更好的體現開閉原則, 整體結構為修改而設計
- 類應依賴于介面, 不依賴實作, 可以隔離修改, 符合依賴倒置原則, 類似策略模式
- 系統
- 構造與使用分開, 通過依賴注入等
- 通過AOP, Proxy的方式, 無侵入性的插入邏輯
- 系統如果充分模塊化, 領域之間相互直接松耦合, 最為理想, 就可以通過測驗來驅動
- DSL的使用可以平衡領域與技術
- 迭代
- 代碼是在不斷迭代中進步的, 比如通過抽函式, 運用設計模式等
- 并發
- 應該盡可能分離并發代碼與其他代碼
- 盡可能讓執行緒之間獨立, 不要有共享
- 執行緒模型有典型的生產者與消費者, 讀者與作者, 宴席哲學家模型, 分別用來說明互斥, 讀寫, 競爭死鎖等特例
- 鎖定代碼塊應該盡可能小
- 執行緒最好可插拔, 遵循單一權責, 分離執行緒與其他代碼, 測驗先保證除執行緒之外的邏輯
- 最后一張通過實體來介紹并發代碼如何重構的比較清晰
- 逐步改進
- 這片使用了一個例子, 先采用蠻干進行重構發現越來越難, 后來采用逐步的辦法, 還寫的很細節, 但是比較Tricky的地方是, 這里提出的每步改動都通過測驗來印證, 然后保證測驗通過, 或者補充一個測驗, 讓代碼通過, 但是所謂TDD部分只有思想, 老代碼既然寫的不好, 測驗是如何出來的, 如何做到Cover全的. 如果假設在一個有比較好測驗覆寫的基礎上重構, 我感覺即便蠻力也不會太差...
- JUnit
- 比較神奇的一章, 感覺更像是用一個演算法類的重構來說明如何把代碼改簡單, 但是開頭先介紹了100%覆寫的Junit測驗長什么樣子... 感覺作者是想說以前的人做的還不錯, 都用JUnit給覆寫全了, 還是可以重構的更漂亮的, 所以感覺標題不太好
- 重構SerialDate
- 跟上一個對比, 這個測驗覆寫不全, 所以在補測驗的程序中進行重構, 還發現了缺陷, 又最終把代碼改清晰了. 不過同樣由于太過細節, 一些重構理論也很教條, 如果想通過看別人重構的例子來學習如果寫簡潔的代碼, 不如直接看優秀的代碼是怎么寫的
- 味道
- 里面類比了很多Bad Smell, 同樣是很細節, 比如太多, 太死, 太復雜, 重復, 不一致, 耦合, 測驗不足等, 提供的Tips大多的中心思想就是抽, 封裝, 單一職責, 命名清晰
- 提到使用*來避免過長import, 不過這個與現在流行的lint檢測違背, 可以因為以前都是手動, 通配符簡單吧.
- 不要通過繼承來使用常量, 還提到不要用靜態常量, 用列舉, 這個也與當前的思想有出入, 現在經常將Java的enum太冗余, 不必要時可以用常量
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/228918.html
標籤:設計模式
