### 為何重構
我不想把重構說成治百病的萬靈丹,它絕對不是所謂的「銀彈」[1]。不過它的確很有價值,雖不是一顆銀子彈卻是一把「銀鉗子」,可以幫助你始終良好地控制自己的代碼。重構是個工具,它可以(并且應該)為了以下數個目的而被運用。
[1]譯注:「銀彈」(silver bullet)是美國家喻戶曉的比喻。美國民間流傳月圓之夜狼人 出沒,只有以純銀子彈射穿狼人心臟,才能制服狼人。
**「重構」改進軟件設計**
如果沒有重構,程序的設計會逐漸腐敗變質。當人們只為短期目的,或是在完全理解整體設計之前,就貿然修改代碼,程序將逐漸失去自己的結構,程序員愈來愈難通過閱讀源碼而理解原本設計。重構很像是在整理代碼,你所做的就是讓所有東西回到應該的位置上。代碼結構的流失是累積性的。愈難看出代碼所代表的設計意涵,就愈難保護其中設計,于是該設計就腐敗得愈快。經常性的重構可以幫助代碼維持自己該有的形態。
同樣完成一件事,設計不良的程序往往需要更多代碼,這常常是因為代碼在不同的地方使用完全相同的語句做同樣的事。因此改進設計的一個重要方向就是消除重復代碼(Duplicate Code)。這個動作的重要性著眼于未來。代碼數量減少并不會使系統運行更快,因為這對程序的運行軌跡幾乎沒有任何明顯影響。然而代碼數量減少將使未來可能的程序修改動作容易得多。代碼愈多,正確的修改就愈困難,因為有更多代碼需要理解。你在這兒做了點修改,系統卻不如預期那樣工作,因為你未曾修改另一處——那兒的代碼做著幾乎完全一樣的事情,只是所處環境略有不同。 如果消除重復代碼,你就可以確定代碼將所有事物和行為都只表述一次,惟一一次,這正是優秀設計的根本。
**「重構」使軟件更易被理解**
從許多角度來說,所謂程序設計,便是與計算機交談。你編寫代碼告訴計算機做什么事,它的響應則是精確按照你的指示行動。你得及時填補「想要它做什么」和「告 訴它做什么」之間的縫隙。這種編程模式的核心就是「準確說出吾人所欲」。除了計算機外,你的源碼還有其他讀者:數個月之后可能會有另一位程序員嘗試讀懂你的代碼并做一些修改。我們很容易忘記這第二位讀者,但他才是最重要的。計算機是否多花了數個鐘頭進行編譯,又有什么關系呢?如果一個程序員花費一周時間來修改某段代碼,那才關系重大——如果他理解你的代碼,這個修改原本只需一小時。
問題在于,當你努力讓程序運轉的時候,你不會想到未來出現的那個開發者。是的,是應該改變一下我們的開發節奏,對代碼做適當修改,讓代碼變得更易理解。重構可以幫助我們讓代碼更易讀。一開始進行重構時,你的代碼可以正常運行,但結構不夠理想。在重構上花一點點時間,就可以讓代碼更好地表達自己的用途。這種編程模式的核心就是「準確說出你的意思」。
關于這一點,我沒必要表現得如此無私。很多時候那個「未來的開發者」就是我自己。此時重構就顯得尤其重要了。我是個很懶惰的程序員,我的懶惰表現形式之一就是:總是記不住自己寫過的代碼。事實上對于任何立可查閱的東西我都故意不去記它,因為我怕把自己的腦袋塞爆。我總是盡量把該記住的東西寫進程序里頭,這樣我就不必記住它了。這么一來我就不必太擔心Old Peculier(譯注:一種有名的麥芽酒〉[Jackson]殺光我的腦細胞。
這種可理解性還有另一方面的作用。我利用重構來協助我理解不熟悉的代碼。當我看到不熟悉的代碼,我必須試著理解其用途。我先看兩行代碼,然后對自己說:『噢, 是的,它做了這些那些……』。有了重構這個強大武器在手,我不會滿足于這么一點腦中體會。我會真正動手修改代碼,讓它更好地反映出我的理解,然后重新執行,看它是否仍然正常運作,以此檢驗我的理解是否正確。
一開始我所做的重構都像這樣停留在細枝末節上。隨著代碼漸趨簡潔,我發現自己可以看到一些以前看不到的設計層面的東西。如果不對代碼做這些修改,也許我永遠看不見它們,因為我的聰明才智不足以在腦子里把這一切都想像出來。Ralph Johnson把這種「早期重構」描述為「擦掉窗戶上的污垢,使你看得更遠」。研究代碼時我發現,重構把我帶到更高的理解層次上。如果沒有重構,我達不到這種層次。
- 譯序 by 侯捷
- 譯序 by 熊節
- 序言
- 前言
- 章節一 重構,第一個案例
- 起點
- 重構的第一步
- 分解并重組statement()
- 運用多態(Polymorphism)取代與價格相關的條件邏輯
- 結語
- 章節二 重構原則
- 何謂重構
- 為何重構
- 「重構」助你找到臭蟲(bugs)
- 何時重構
- 怎么對經理說?
- 重構的難題
- 重構與設計
- 重構與性能(Performance)
- 重構起源何處?
- 章節三 代碼的壞味道
- Duplicated Code(重復的代碼)
- Long Method(過長函數)
- Large Class(過大類)
- Long Parameter List(過長參數列)
- Divergent Change(發散式變化)
- Shotgun Surgery(散彈式修改)
- Feature Envy(依戀情結)
- Data Clumps(數據泥團)
- Primitive Obsession(基本型別偏執)
- Switch Statements(switch驚悚現身)
- Parallel Inheritance Hierarchies(平行繼承體系)
- Lazy Class(冗贅類)
- Speculative Generality(夸夸其談未來性)
- Temporary Field(令人迷惑的暫時值域)
- Message Chains(過度耦合的消息鏈)
- Middle Man(中間轉手人)
- Inappropriate Intimacy(狎昵關系)
- Alternative Classes with Different Interfaces(異曲同工的類)
- Incomplete Library Class(不完美的程序庫類)
- Data Class(純稚的數據類)
- Refused Bequest(被拒絕的遺贈)
- Comments(過多的注釋)
- 章節四 構筑測試體系
- 自我測試代碼的價值
- JUnit測試框架
- 添加更多測試
- 章節五 重構名錄
- 重構的記錄格式
- 尋找引用點
- 這些重構準則有多成熟
- 章節六 重新組織你的函數
- Extract Method(提煉函數)
- Inline Method(將函數內聯化)
- Inline Temp(將臨時變量內聯化)
- Replace Temp with Query(以查詢取代臨時變量)
- Introduce Explaining Variable(引入解釋性變量)
- Split Temporary Variable(剖解臨時變量)
- Remove Assignments to Parameters(移除對參數的賦值動作)
- Replace Method with Method Object(以函數對象取代函數)
- Substitute Algorithm(替換你的算法)
- 章節七 在對象之間搬移特性
- Move Method(搬移函數)
- Move Field(搬移值域)
- Extract Class(提煉類)
- Inline Class(將類內聯化)
- Hide Delegate(隱藏「委托關系」)
- Remove Middle Man(移除中間人)
- Introduce Foreign Method(引入外加函數)
- Introduce Local Extension(引入本地擴展)
- 章節八 重新組織數據
- Self Encapsulate Field(自封裝值域)
- Replace Data Value with Object(以對象取代數據值)
- Change Value to Reference(將實值對象改為引用對象)
- Replace Array with Object(以對象取代數組)
- Replace Array with Object(以對象取代數組)
- Duplicate Observed Data(復制「被監視數據」)
- Change Unidirectional Association to Bidirectional(將單向關聯改為雙向)
- Change Bidirectional Association to Unidirectional(將雙向關聯改為單向)
- Replace Magic Number with Symbolic Constant(以符號常量/字面常量取代魔法數)
- Encapsulate Field(封裝值域)
- Encapsulate Collection(封裝群集)
- Replace Record with Data Class(以數據類取代記錄)
- Replace Type Code with Class(以類取代型別碼)
- Replace Type Code with Subclasses(以子類取代型別碼)
- Replace Type Code with State/Strategy(以State/strategy 取代型別碼)
- Replace Subclass with Fields(以值域取代子類)
- 章節九 簡化條件表達式
- Decompose Conditional(分解條件式)
- Consolidate Conditional Expression(合并條件式)
- Consolidate Duplicate Conditional Fragments(合并重復的條件片段)
- Remove Control Flag(移除控制標記)
- Replace Nested Conditional with Guard Clauses(以衛語句取代嵌套條件式)
- Replace Conditional with Polymorphism(以多態取代條件式)
- Introduce Null Object(引入Null 對象)
- Introduce Assertion(引入斷言)
- 章節十一 處理概括關系
- Pull Up Field(值域上移)
- Pull Up Method(函數上移)
- Pull Up Constructor Body(構造函數本體上移)
- Push Down Method(函數下移)
- Push Down Field(值域下移)
- Extract Subclass(提煉子類)
- Extract Superclass(提煉超類)
- Extract Interface(提煉接口)
- Collapse Hierarchy(折疊繼承關系)
- Form Template Method(塑造模板函數)
- Replace Inheritance with Delegation(以委托取代繼承)
- Replace Delegation with Inheritance(以繼承取代委托)
- 章節十二 大型重構
- 這場游戲的本質
- Tease Apart Inheritance(梳理并分解繼承體系)
- Convert Procedural Design to Objects(將過程化設計轉化為對象設計)
- Separate Domain from Presentation(將領域和表述/顯示分離)
- Extract Hierarchy(提煉繼承體系)
- 章節十三 重構,復用與現實
- 現實的檢驗
- 為什么開發者不愿意重構他們的程序?
- 現實的檢驗(再論)
- 重構的資源和參考資料
- 從重構聯想到軟件復用和技術傳播
- 結語
- 參考文獻
- 章節十四 重構工具
- 使用工具進行重構
- 重構工具的技術標準(Technical Criteria )
- 重構工具的實用標準(Practical Criteria )
- 小結
- 章節十五 集成
- 參考書目