最近在閑暇之余重(第)溫(一..次)此書, 首先能感受到的, 無論你是新程序員還是老程序員, 這本書都已經(jīng)不具備太多的可讀性了.
由于本書成書年代久遠(yuǎn), 那個時候軟件行業(yè)還不夠發(fā)達(dá), 面向?qū)ο筮€沒有被大數(shù)人理解, 加之編譯器也非常落后, 設(shè)計模式也不深入人心, 所以文中提供的所謂重構(gòu)的心法, 在當(dāng)時或許有一些意義. 而今看來, 整書400多頁的文字, 主要的思想就是「抽」, 無論是類, 接口, 方法, 邏輯, 還是參數(shù). 但是為什么會有這么大的篇幅, 其中一半的內(nèi)容是教你如何在一個IDE功能匱乏的年代, 以一個出錯率更低的順序, 來進(jìn)行上面所說的多種抽象操作.
當(dāng)然我去除了大量書中已經(jīng)沒有任何價值的點(diǎn)后, 總結(jié)了如下的一些內(nèi)容, 應(yīng)該大多數(shù)大家已經(jīng)在工作實踐中已有體會, 主要還是總結(jié)一下, 溫故知新吧.
- 思想方面
- 如果發(fā)現(xiàn)加特性很難, 就需要重構(gòu)
- 重構(gòu)前需要想想有沒有可靠的測試機(jī)制
- 重構(gòu)要微小步伐, 容易發(fā)現(xiàn)錯誤
- Extract method是最簡單的重構(gòu)
- 寫出機(jī)器認(rèn)識的代碼容易, 寫出人類容易理解的才是應(yīng)該的, 最大的影響就是命名
- 做移動重構(gòu)時, 最好先復(fù)制粘貼, 測后再刪除舊的代碼
- 對于另外對象的switch往往可以通過繼承, 多態(tài)來取代
- 三次遇到不合理的, 可能就是重構(gòu)的時候了
- 重構(gòu)往往通過加隔離層進(jìn)行
- 不要過早發(fā)布接口
- 性能優(yōu)化應(yīng)該基于良好的代碼結(jié)構(gòu), 性能瓶頸往往只會在很小的代碼片段里
- 每次遇到一個bug, 盡量寫個單元測試覆蓋
- 測試集中覆蓋邊緣case
- 代碼壞味道與手段
- 重復(fù)代碼
- 大函數(shù)
- 大類
- 太多參數(shù)
- 加新功能修改多個函數(shù)
- 加新功能需要改很多類
- 函數(shù)過度依賴其他類
- 同樣的幾個參數(shù)到處出現(xiàn)
- 大量同樣的基本類型做參數(shù)
- 很多switch
- 子類需平行同時添加
- 沒人用的類
- 沒太大用處的抽象, 參數(shù), 命名
- 臨時變量命名模糊
- 消息鏈過長, 對象轉(zhuǎn)換過多
- 過度代理
- 注釋太多
- 數(shù)據(jù)
- 臨時變量被賦值多次, 則可以抽成方法
- 有時候需要提取多個臨時變量對邏輯進(jìn)行解釋, 當(dāng)然更好的可以再抽成方法
- Java按值傳遞, 本質(zhì)上是對象的引用按值傳遞
- 移除中間人與隱藏委托是相輔相成的
- 如果類無法更改, 就寫個函數(shù)包裝他, 類似于工廠方法?
- 如果不能修改, Adapter模式常常用于擴(kuò)展方法功能
- 子類修改某些變量的獲取可以通過自封裝, 把變量抽象成函數(shù)
- 成員如果不用改變, 就由引用變?yōu)橹祵ο? 即immutable對象
- 當(dāng)業(yè)務(wù)復(fù)雜度變高需要將類之間單向關(guān)系改為雙向綁定關(guān)系, 甚至一對多與多對一的關(guān)系
- 雙向綁定可能會造成很多僵尸對象, 增加復(fù)雜度與空間占用, 所以只有真正需要的時候用, 刪除可以通過將內(nèi)部綁定查詢轉(zhuǎn)移為傳參, 再在調(diào)用的位置進(jìn)行判斷是否為僵尸對象
- 不要使用Magic Number, 常量即可以優(yōu)化儲存, 又可讀性高
- 封裝字段可以控制字段的讀寫, 稱之為數(shù)據(jù)隱藏
- 對于返回集合的函數(shù)需要返回只讀副本, 如果需要修改, 則可另行提供修改方法
- 原始類型替換成類, 有類型字段的類轉(zhuǎn)換為多個子類, 或者抽象成策略模式, 從而合理改變數(shù)據(jù)結(jié)構(gòu)
- 如果子類只有常量, 可以抽成變量放父類, 再添加工廠方法創(chuàng)建, 降低復(fù)雜度
- 條件表達(dá)式
- 條件表達(dá)式很復(fù)雜的時候, 可以將條件, 執(zhí)行代碼進(jìn)行封裝, 更表意
- 嵌套很深的條件表達(dá)式的每個結(jié)果都作為返回值的話, 可以簡化為多個if+return, 類似于swift里面的guard, 或者kotlin里面的?:return, 書里叫衛(wèi)語句(Guard Clauses)
- 嵌套很深的條件表達(dá)式可以將范圍大的if反向, 實現(xiàn)提前return
- switch表達(dá)式有時候可以通過多態(tài)來取代
- 函數(shù)調(diào)用
- 函數(shù)最好讀寫分離
- 抽象重復(fù)性的函數(shù)用以復(fù)用
- switch構(gòu)造可以替換成工廠方法
- 從對象內(nèi)拿出參數(shù)再傳遞給某函數(shù)不如直接將對象傳入函數(shù)
- 當(dāng)函數(shù)的參數(shù)來自另外一個函數(shù)時, 也可以刪掉參數(shù), 把函數(shù)調(diào)用挪進(jìn)去
- 函數(shù)參數(shù)過多可以同對象替代
- 如果不需要設(shè)置就不要提供set方法, 變量都需要為final
- 有時候用拋異常替代返回錯誤碼, 如果程序無法繼續(xù)進(jìn)行的話
- 如果可以條件邏輯避免Runtime異常, 應(yīng)不要無腦try/catch
- 繼承關(guān)系
- 子類同樣的方法應(yīng)該向上提, 并將不同的地方抽象為抽象方法分別繼承
- 子類構(gòu)造復(fù)制父類字段應(yīng)該通過父類構(gòu)造, super
- 超類與子類差不多, 就合并
- 邏輯相同, 類型不同可以通過模板函數(shù), 模板類解決
- 不能濫用繼承, 組合代理好過繼承, 但也有特殊, 如果需要使用委托函數(shù)所有函數(shù)或者大量, 則就需要繼承, 不過這一點(diǎn)在kotlin里面也可以通過by來代理
- 復(fù)雜的重構(gòu)
- 整理復(fù)雜的繼承關(guān)系, 該抽接口抽接口, 摘出更多深層子類
- 領(lǐng)域與表述分離, 就是UI與邏輯分離
- 過程抽象為對象
- 提煉繼承體系
|