Martin Fowler 等:refactoring——improving The Design Of Existing Code@2002 (第1版)

以下是從提供的資料中提取的主要論點及其詳細解釋:

關於「重構:改善既有程式的設計」(Refactoring: Improving the Design of Existing Code)一書的主要論點

本書的核心思想圍繞著「重構」(Refactoring) 這一概念,並闡述了它在軟體開發,特別是物件導向軟體開發中的重要性、方法和實際應用。主要論點包括:

  1. 重構的定義與目的

    • 定義: 重構是一種改變軟體系統內部結構的過程,其目的在於使其更容易理解和修改,同時不改變程式碼的外部行為。它是一種有紀律的方式,用以清理程式碼並將引入錯誤的風險降到最低。
    • 目的: 重構的根本目標是改善程式碼的設計,使其在寫好之後也能持續演進。這與傳統的「先設計,後編碼」流程不同,重構允許設計在開發過程中不斷精煉和調整。透過重構,混亂的程式碼也能被重塑為結構良好的設計。
  2. 重構的核心價值與效益

    • 改善軟體設計: 沒有重構,程式的設計會隨著時間和修改(無論是為了短期目標還是由於不完全理解既有設計)而衰退,結構變得模糊不清。重構就像清理程式碼,移除不屬於其位置的部分,幫助程式碼保持其形狀和結構,對抗腐敗。特別是消除重複程式碼,確保同一件事只被表達一次,這是良好設計的精髓,能大幅降低未來修改的成本。
    • 使軟體更容易理解: 程式碼不僅是給電腦執行的,更是給人閱讀和理解的。重構透過將程式碼分解為更小、命名更有意義的部分,使程式碼更清楚地傳達其意圖。這對未來的開發者(通常也是自己)至關重要,能顯著減少理解程式碼所需的時間和精力。作者強調,當他閱讀不熟悉的程式碼時,會透過重構來加深理解,並將這種理解嵌入程式碼中。
    • 協助找到錯誤: 重構的過程迫使開發者深入理解程式碼的運作方式。這種對程式結構的釐清過程往往會暴露出原先可能被忽略的錯誤。程式碼越清晰,潛在的錯誤也越容易被發現。
    • 加速程式開發: 雖然重構本身不直接添加新功能,看似額外開銷,但從長遠來看,它能透過維持或改善設計來提升開發速度。良好的設計是快速軟體開發的關鍵,因為它減少了因設計不良導致的錯誤查找和修復時間,也使新增功能變得更容易。在許多情況下,先重構以使新增功能更容易,比直接在既有混亂程式碼上修改更為快捷。
  3. 重構的時機與流程

    • 持續進行而非排程活動: 作者反對將重構視為一個獨立的、需要特別排程的活動(例如每幾個月撥出兩週)。重構應該是開發過程中的一部分,以小規模、頻繁的方式進行。
    • 「三次定律」(The Rule of Three): 第一次做某事,就直接做;第二次做類似的事,會感覺到重複,但還是做了;第三次做類似的事,就應該進行重構。這是判斷何時應消除重複程式碼的實用啟發。
    • 常見的重構時機:
      • 新增功能時: 當發現既有程式碼的結構不適合新增功能時,應先重構程式碼使其易於添加新功能,然後再添加。
      • 修復錯誤時: 錯誤的出現往往是程式碼不夠清晰的訊號。在理解程式碼以修復錯誤的過程中,可以透過重構來增強理解。
      • 程式碼審查時: 程式碼審查是傳播知識和改進程式碼品質的好時機。將重構融入審查過程,可以將審查意見立即轉化為程式碼的改進。結對程式設計(Pair Programming)更是將此概念推到極致,將重構內嵌於日常開發中。
    • 重構的節奏: 重構的過程強調小步驟和頻繁測試。基本的節奏是:測試 -> 小改變 -> 測試 -> 小改變。這種節奏使得即使引入錯誤,也能非常容易地找到並修復。
  4. 重構與測試的緊密關係

    • 測試是重構的先決條件: 進行重構的關鍵先決條件是擁有堅實的測試套件。測試套件能夠驗證重構過程中程式碼的外部行為是否保持不變。
    • 自我檢查的測試: 測試必須是自動化且自我檢查的,無需人工檢查輸出。這使得頻繁執行測試成為可能。
    • 頻繁執行測試: 測試應該被頻繁地執行,理想情況下每次編譯後都執行。頻繁執行測試可以迅速捕捉到引入的錯誤,由於每次修改都非常小,錯誤的定位和修復成本極低。
    • 測試是強大的錯誤偵測器: 透過頻繁測試,開發者可以大幅減少花在偵錯上的時間,提高生產力。
    • 測試驅動開發思維: 甚至建議在編寫生產程式碼之前先寫測試。寫測試迫使開發者思考所需功能的介面和預期行為,有助於更好地設計程式碼。
    • 針對錯誤報告撰寫測試: 當發現錯誤時,應先為該錯誤撰寫一個能重現問題的單元測試,這有助於定位錯誤並確保未來不再出現類似問題。
  5. 程式碼的「壞味道」(Bad Smells)

    • 本書提供了一系列徵兆或「壞味道」,用以識別需要重構的程式碼。這些味道是經驗豐富的開發者在程式碼中識別出的模式,表明潛在的設計問題或改進機會。
    • 常見的壞味道包括: 重複程式碼(Duplicated Code)、過長的方法(Long Method)、過大的類別(Large Class)、過長的參數列表(Long Parameter List)、散彈式修改(Shotgun Surgery,一種類型的修改需要修改許多類別)、依戀情結(Feature Envy,方法過度依賴其他類別的資料)、資料泥團(Data Clumps,多個資料項目總是結伴出現)、Switch 陳述句(Switch Statements,違反多型原則)等。
    • 每種壞味道通常都對應著一或多個建議的重構手法,可以作為開始重構的入口。
  6. 重構與設計的演進

    • 重構改變了設計在軟體開發中的角色。它使得設計不是一次性完成的活動,而是一個持續演進的過程。
    • 重構降低了前期設計的壓力:開發者無需試圖在前期就找到「最佳」或「最靈活」的設計,因為知道未來可以透過重構輕鬆修改設計。
    • 鼓勵採用簡單設計:既然修改設計的成本降低了,開發者可以更有信心先實現「最簡單能用的東西」(simplest thing that can possibly work)。過度的猜測性泛化或提前引入不必要的複雜性可以被避免,因為未來需要時再重構即可。
  7. 重構與效能

    • 重構可能導致程式在短期內變慢,因為它通常會增加間接性(例如方法呼叫)。
    • 然而,良好的重構使程式碼更容易進行效能調優。作者提倡「先寫可調優的軟體,再進行調優」。
    • 應在開發後期,使用分析工具(profiler)來識別效能瓶頸(通常集中在程式碼的一小部分),然後專注於這些熱點進行優化。重構後的清晰程式碼使得更容易理解優化選項並有效地應用優化技術。
  8. 重構的挑戰與限制

    • 資料庫: 資料庫模式(schema)的變更與程式碼的重構耦合較深,且涉及資料遷移,是重構中的一個難點。可以考慮引入隔離層來處理物件模型與資料庫模型的分離。
    • 改變介面: 許多重構會改變方法的介面。對於已被廣泛使用且無法修改所有呼叫者的介面(即 Published Interface),重構會更加困難,可能需要保留舊介面並使其委派到新介面。
    • 難以重構的設計變更: 有些設計決策可能非常核心,導致後續透過重構改變它們變得異常困難。雖然這種情況相對罕見,但有時需要在前期設計中投入更多精力。
  9. 不應進行重構的時機

    • 當現有程式碼過於混亂且充滿錯誤,無法穩定下來進行測試時,重寫可能比重構更為合適。
    • 當專案非常接近截止日期時,重構可能帶來的短期生產力下降風險過高。然而,作者也強調,通常感到時間不夠時,反而可能是需要重構以提升效率的訊號。
  10. 重構的工具支援

    • 自動化的重構工具可以極大地提升重構效率和安全性。工具可以自動完成許多重複性、易錯的步驟,並驗證重構的安全性。
    • Smalltalk 環境的 Refactoring Browser 是這方面的先驅。Java 也需要類似工具來支援開發者。
    • 工具的技術要求包括程式資料庫(以便查找引用)和解析樹(以便精確修改程式碼)。實用標準包括速度、支援撤銷操作以及與其他開發工具的整合。

這些論點共同構成了「重構」一書的核心內容,強調了重構作為一種持續改進軟體設計的實踐,以及它在提升程式碼品質、可理解性和開發效率方面的關鍵作用,同時也指出了實施重構所需的前提條件(測試)和可能遇到的挑戰。