重構(Refactoring)技巧讀書筆記 之二<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
General Refactoring Tips, Part 2
?
本文繼續《重構(Refactoring)技巧讀書筆記 之一》,重構的確是未來軟件工程師需要掌握的一項技能。目前一些支持.Net的重構工具,如ReSharper for VS.Net v1.0、Borland Together for VS.Net v2.0和VS.Net 2005等,只能支持一些有限的、比較簡單的重構策略。大量的重構策略需要軟件工程師清晰的了解,人工為主,運用重構工具輔助進行。(注:本文重構策略的名稱及其大部分內容來自《重構-改善既有代碼的設計》一書,Martin Fowler 著,侯捷等譯)。
下面的內容延續上一節的內容,其中提及的一些代碼壞味道(Bad Smell in Codes)及其重構策略相對而言要比較麻煩一些。
?
一、代碼壞味道(Bad Smell in Codes)及其重構策略
5.Divergent Change(發散式變化)
現象:當某個Class因為外部條件的變化或者客戶提出新的功能要求等時,每次修改要求我們更新Class中不同的方法。不過這種情況只有在事后才能覺察到,因為修改都是在事后發生的么(廢話)。
重構策略:將每次因同一條件變化,而需要同時修改的若干方法通過Extract Class將它們提煉到一個新Class中。實現目標是:每次變化需要修改的方法都在單一的Class中,并且這個新的Class內所有的方法都應該與這個變化相關。
?
6.Shotgun Surgery(霰彈式修改)
現象:當外部條件發生變化時,每次需要修改多個Class來適應這些變化,影響到很多地方。就像霰彈一樣,發散到多個地方。
重構策略:使用Move Method和Move Field將Class中需要修改的方法及成員變量移植到同一個Class中。如果沒有合適的Class,則創建一個新Class。實現目標是,將需要修改的地方集中到一個Class中進行處理。
?
比較Divergent Change(發散式變化)和Shotgun Surgery(霰彈式修改):
前者指一個Class受到多種外部變化的影響。而后者指一種變化需要影響到多個Class需要修改。都是需要修理的對象。
?
7.Feature Envy(依戀情結)
現象:Class中某些方法“身在曹營心在漢”,沒有安心使用Class中的成員變量,而需要大量訪問另外Class中的成員變量。這樣就違反了對象技術的基本定義:將數據和操作行為(方法)包裝在一起。
重構策略:使用Move Method將這些方法移動到對應的Class中,以化解其“相思之苦”,讓其牽手。
?
8.Data Clumps(數據泥團)
現象:指一些相同數據項目(Data Items),如Class成員變量和方法中參數列表等,在多個Class中多次出現,并且這些數據項目有其內在的聯系。
重構策略:通過使用Introduce Parameter Object(創建新的參數對象取代這些參數)或Preserve Whole Object(使用已存在的對象取代這些參數),實現使用對象代替Class成員變量和方法中參數列表,清除數據泥團,使代碼簡潔,也提高維護性和易讀性。
?
9.Primitive Obsession(基本型偏執狂)
現象:在Class中看到大量的基本型數據項目(Data Item),如Employee類中有大量的數據成員,Employee#, FirstName, MiddleName, LastName, Address, State, City, Street, Zip, OfficePhone, CellPhone, Email……等等。
重構策略:使用Extract Class(提煉新類)或Preserve Whole Object(使用已存在的對象取代這些參數),實現使用對象代替基本型數據項目(Data Item)。如上述Employee類中就可分別提煉出EmployeeName和EmployeeContact兩個新類。
?
10.Switch Statements(Switch語句)
現象:同樣的Switch語句出現在不同的方法或不同的Class中,這樣當需要增加新的CASE分支或者修改CASE分支內語句時,就必須找到所有的地方,然后進行修改。這樣,就比較麻煩了。
重構策略:(1)首先采用Extract Method將Switch語句提煉到一個獨立的函數。
(2)然后以Move Method搬移到需要多態性(Polymorphism)的Superclass里面或者是構建一個新的Superclass。
(3)進一步使用Replace Type Code with Subclasses或者Replace Type Code with State/Strategy。這步就比較麻煩些,不過記住如下基本規則:這里一般有3個Class分別為Source Class、Superclass和Subclass。
Source Class:
l???????? 使用Self Encapsulate Field,將Type Code成員變量封裝起來,也就是建立對應的Setter/Getter函數。
l???????? 在Source Class中增加一個Superclass類型的成員變量,用來存放Subclass實例對象。
l???????? 在Source Class中的Getter函數,通過調用Superclass的Abstract Query函數來完成。
l???????? 在Source Class中的Setter函數,通過調用Superclass中的Static工廠化方法來獲取合適的Subclass實例對象。
?
Superclass:
新建的一個Class(注:就是上面通過Move Method搬移生成的Superclass),根據Type Code的用途命名該Class,作為Superclass。
l???????? 在Superclass中建立一個Abstract Query函數,用來獲取Subclass的Type Code。
l???????? 在Superclass中創建Static工廠化方法生產對應的Subclass對象,這里會存在一個Switch語句(不要再動腦筋來重構這個Switch語句了,這個Switch語句不會在多處重復存在,并且這里用于決定創建何種Subclass對象,這是完全可以接受的)。
?
Subclass:
l???????? 根據每一個Switch/Type分支,建立對應的Subclass,并且Subclass的命名可以參考Switch/Type分支的命名。
l???????? 在每一個Subclass中重載Superclass的Abstract Query函數,返回特定的Type Code。
(4)現在Superclass仍然存在Switch分支,是時候輪到Replace Conditional with Polymorphism上場了。具體而言,就是在每一個Subclass中創建重載方法(注:該方法是Superclass中含有Switch語句的方法),并將Superclass中Switch語句對應的Case分支剪切過來。最后將Superclass中該方法初象化Abstract,并清除Switch語句及其所有的Case分支。
這樣就完成了整個重構過程,這個比較麻煩。
?
注:并不是一看到Switch語句及CASE分支,就馬上/偏執狂采用上述重構策略進行重構,畫蛇添足或吃虧不討好(個人觀點)。一般而言,只有看到多處出現相同的Switch語句時,才應該考慮進行重構。
?
References:
1, Rickie, 重構(Refactoring)技巧讀書筆記 之一