### 怎么對經理說?
「該怎么跟經理說重構的事?」這是我最常被問到的問題之一。如果這位經理懂技術,那么向他介紹重構應該不會很困難。如果這位經理只對質量感興趣,那么問題就集中到了「質量」上面。此時,在復審過程中使用重構,就是一個不錯的辦法。 大量研究結果顯示,「技術復審」是減少錯誤、提高開發速度的一條重要途徑。隨便找一本關于復審、審査或軟件開發程序的書看看,從中找些最新引證,應該可以讓大多數經理認識復審的價值。然后你就可以把重構當作「將復審意見引入代碼內」的方法來使用,這很容易。
當然,很多經理嘴巴上說自己「質量驅動」,其實更多是「進度驅動」。這種情況下我會給他們一個較有爭議的建議:不要告訴經理!
這是在搞破壞嗎?我不這樣想。軟件開發者都是專業人士。我們的工作就是盡可能快速創造出髙效軟件。我的經驗告訴我,對于快速創造軟件,重構可帶來巨大幫助。如果需要添加新功能,而原本設計卻又使我無法方便地修改,我發現先「進行重構」再「添加新功能」會更快些。如果要修補錯誤,我需得先理解軟件工作方式,而我發現重構是理解軟件的最快方式。受進度驅動的經理要我盡可能快速完事,至于怎么完成,那就是我的事了。我認為最快的方式就是重構,所以我就重構。
**間接層和重構(Indirection and Refactoring)**
——Kent Beck
「計算機科學是這樣一門學科:它相信所有問題都可以通過多一個間接層(Indirection)來解決。」——Dennis DeBruler
由于軟件工程師對間接層如此醉心,你應該不會驚訝大多數重構都為程序引入了更多間接層。重構往往把大型對象拆成數個小型對象,把大型函數拆成數個小型函數。
但是,間接層是一抦雙刃劍。每次把一個東西分成兩汾,你就需要多管理一個東西。如果某個對象委托(delegate)另一對象,后者又委托另一對象,程序會愈加難以閱讀。基于這個觀點,你會希望盡量減少間接層。
別急,伙計!間接層有它的價值。下面就是間接層的某些價值:
- 允許邏輯共享〔To enable sharing of logic)。比如說一個子函數(submethod)在兩個不同的地點被調用,或superclass的某個函數被所有subclass共享。
- 分開解釋「意圖」和「實現](To explain intention and implementation separately)。你可以選擇每個class和函數的名字,這給了你一個解釋自己意圖的機會。class或函數內部則解釋實現這個意圖的作法。如果class和函數內部又以「更小單元的意圖」來編寫,你所寫的代碼就可以『與其結構中的大部分重要信息溝通」。
- 將變化加以隔離(To isolate change)。很可能我在兩個不同地點使用同一對象,其中一個地點我想改變對象行為,但如果修改了它,我就要冒「同時影響兩處」的風險。為此我做出一個subclass,并在需要修改處引用這個subclass。現在,我可以修改這個subclass而不必承擔「無意中影響另一處」的風險。
- 將條件邏輯加以編碼(To encode conditional logic)。對象有一種匪夷所思的機制:多態消息(polymorphic messages),可以靈活彈性而清晰地表達條件邏輯。只要顯式條件邏輯被轉化為消息(message [2])形式,往往便能降低代碼的重復、增加清晰度并提髙彈性。
[2]譯注:此處的「消息」(message)是指面向對象古典論述中的意義。在那種場合中,「調用某個函數(method)」就是「送出消息(message)給某個對象(object)」。
這就是重構游戲:在保持系統現有行為的前提下,如何才能提高系統的質量或降低其成本,從而使它更有價值?
這個游戲中最常見的變量就是:你如何看待你自己的程序。找出一個缺乏「間接層利益」之處,在不修改現有行為的前提下,為它加入一個間接層。現在你獲得了一個更有價值的程序,因為它有較髙的質量,讓我們在明天(未來)受益。
請將這種方法與「小心翼翼的事前設計」做個比較。推測性設計總是試圖在任何一行代碼誕生之前就先讓系統擁有所有優秀質量,然后程序員將代碼塞進這個強健的骨架中就行了。這個過程的問題在于:太容易猜錯。如果運用重構,你就永遠不會面臨全盤錯誤的危險。程序自始至終都能保持一致的行為,而你又有機會為程序添加更多價值不菲的質量。
還有一種比較少見的重構游戲:找出不值得的間接層,并將它拿掉。這種間接層常以中介函數(intermediate methods)形式出現,也許曾經有過貢獻,但芳華已逝。它也可能是個組件,你本來期望在不同地點共享它,或讓它表現出多態性(polymorphism),最終卻只在一處使用之。如果你找到這種「寄生式間接層」,請把它扔掉。如此一來你會獲得一個更有價值的程序,不是因為它取得了更多(先前所列)的四種優秀質量,而是因為它以更少的間接層獲得一樣多的優秀質量。
- 譯序 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 )
- 小結
- 章節十五 集成
- 參考書目