软件配置管理(五)常用重构技巧
文章目錄
- 一、重新組織函數(shù)
- 1.提煉函數(shù)
- 2.內(nèi)聯(lián)函數(shù)
- 3.內(nèi)聯(lián)臨時變量
- 4.以查詢?nèi)〈R時變量
- 5.引入解釋性變量
- 6.分解臨時變量
- 7.移除對參數(shù)的賦值
- 8.以函數(shù)對象取代函數(shù)
- 9.替換算法
- 二、在對象之間搬移特性
- 1.搬移函數(shù)
- 2.搬移字段
- 3.提煉類
- 4.將類內(nèi)聯(lián)化
- 5.隱藏“委托關(guān)系”
- 6.移除中間人
- 7.引入外加函數(shù)
- 8.引入本地擴(kuò)展
- 三、重新組織數(shù)據(jù)
- 1.自封裝字段
- 2.以對象取代數(shù)據(jù)值
- 3.將值對象改為引用對象
- 4.將引用對象改為值對象
- 5.以對象取代數(shù)組
- 6.復(fù)制“被監(jiān)視的數(shù)據(jù)”
- 7.將單向關(guān)聯(lián)改為雙向關(guān)聯(lián)
- 8.將雙向關(guān)聯(lián)改為單向關(guān)聯(lián)
- 9.以字面常量取代魔法數(shù)
- 10.封裝字段
- 11.封裝集合
- 12.以數(shù)據(jù)類取代記錄
- 13.以類取代類型🐎
- 14.以子類取代類型🐎
- 15.以State(狀態(tài)模式)/Strategy(策略模式)取代類型🐎
- 16.以字段取代子類
- 四、簡化條件表達(dá)式
- 1.分解條件表達(dá)式
- 2.合并條件表達(dá)式
- 3.合并重復(fù)的條件片段
- 4.移除控制標(biāo)記
- 5.以衛(wèi)語句取代嵌套條件表達(dá)式
- 6.以多態(tài)取代條件表達(dá)式
- 7.引入Null對象
- 8.引入斷言
- 五、 簡化函數(shù)調(diào)用
- 1.函數(shù)改名
- 2.添加參數(shù)
- 3.移除參數(shù)
- 4.將查詢函數(shù)和修改函數(shù)分離
- 5.令函數(shù)攜帶參數(shù)
- 6.以明確函數(shù)取代參數(shù)
- 7.保持對象完整
- 8.以函數(shù)取代參數(shù)
- 9.引入?yún)?shù)對象
- 10.移除設(shè)值函數(shù)
- 11.隱藏函數(shù)
- 12.以工廠函數(shù)取代構(gòu)造函數(shù)。
- 13.封裝向下轉(zhuǎn)型
- 14.以異常取代錯誤碼
- 15.以測試取代異常
- 六、處理概括關(guān)系
- 1.字段上移
- 2.函數(shù)上移
- 3.構(gòu)造函數(shù)本體上移
- 4.函數(shù)下移
- 5.字段下移
- 6.提煉子類
- 7.提煉超類
- 8.提煉接口
- 9.折疊繼承體系
- 10.塑造模板函數(shù)。
- 11.以委托取代繼承
- 12.以繼承取代委托
- 七、大型重構(gòu)
- 1.梳理并分解繼承體系
- 2.將過程化設(shè)計轉(zhuǎn)化為對象設(shè)計
- 3.將領(lǐng)域和表述/顯示分離
- 4.提煉繼承體系
一、重新組織函數(shù)
1.提煉函數(shù)
將一段代碼組織并獨立出來,放進(jìn)一個獨立函數(shù)中,并讓函數(shù)名稱解釋用途。
2.內(nèi)聯(lián)函數(shù)
若一個函數(shù)的函數(shù)體足以解釋函數(shù)作用,其解釋性與函數(shù)名稱相差無幾。那么可以移除函數(shù),將函數(shù)體插入函數(shù)調(diào)用點。
3.內(nèi)聯(lián)臨時變量
若一個臨時變量只被一個簡單的表達(dá)式賦值一次,且這個變量妨礙了其他的重構(gòu)手段。那么可以將所有對該變量的引用替換為表達(dá)式本身。
4.以查詢?nèi)〈R時變量
若在一個函數(shù)中,一個表達(dá)式的運算結(jié)果被賦值給了一個臨時變量,而大量的臨時變量會導(dǎo)致理解性變差,而且臨時變量無法被其他函數(shù)使用。此時可以把表達(dá)式獨立為一個函數(shù)(稱為查詢式),并將所有臨時變量的引用替換為對函數(shù)的調(diào)用。
5.引入解釋性變量
在復(fù)雜的表達(dá)式中,可以將表達(dá)式的中間結(jié)果賦給一個臨時變量,用變量名解釋表達(dá)式的用途。
6.分解臨時變量
在一段程序中,某個臨時變量被多次賦值,但每次賦值的原因都不同(既不是循環(huán)變量,也不是用來收集計算結(jié)果的變量)。此時可以針對每次賦值都創(chuàng)造一個獨立的臨時變量,增強(qiáng)解釋性。
7.移除對參數(shù)的賦值
不要對函數(shù)的參數(shù)進(jìn)行賦值,用臨時變量取代被重新賦值的參數(shù)。
8.以函數(shù)對象取代函數(shù)
只要將相對獨立的代碼從大函數(shù)提煉出來,就可以大大提高代碼的可讀性。但局部變量(臨時變量)的存在會增加函數(shù)分解的難度。如果一個函數(shù)中的局部變量泛濫成災(zāi),那么想分解這個函數(shù)是非常困難的。“以查詢替換臨時變量”手法可以幫助減輕負(fù)擔(dān),但有時候還是會發(fā)現(xiàn)根本無法拆解一個需要拆解的函數(shù)。這種情況就應(yīng)該考慮使用函數(shù)對象來解決。
在一個大型函數(shù)中,臨時變量的使用導(dǎo)致無法提煉函數(shù),此時可以將這個函數(shù)放進(jìn)一個單獨的對象中,將臨時變量作為對象的成員變量,這樣就可以將新對象中的大型函數(shù)分解。
示例代碼
9.替換算法
在一個函數(shù)體內(nèi),將某個算法替換為另一個算法。
二、在對象之間搬移特性
1.搬移函數(shù)
在一個類中的某個函數(shù)相較于所在類,其與另一個類有著更加多的聯(lián)系(依戀情結(jié))。可以在其所依戀的類中建立一個有類似行為的新函數(shù),將舊函數(shù)變成一個委托函數(shù),或直接移除。
2.搬移字段
在一個類中的某個字段相較于所在類,其與另一個類有著更加多的聯(lián)系。可以在其目標(biāo)類中新建一個字段,并將對舊字段的引用轉(zhuǎn)移到新字段中。
3.提煉類
若一個類不符合單一職責(zé)原則,那么可以新建一個類,并將相關(guān)的字段和函數(shù)轉(zhuǎn)移。
4.將類內(nèi)聯(lián)化
若一個類的職責(zé)過小,那么可以將這個類中的所有字段和函數(shù)轉(zhuǎn)移到另一個類中,并移除原類。
5.隱藏“委托關(guān)系”
“封裝”意味每個對象都應(yīng)該盡可能少了解系統(tǒng)的其他部分。
如果某個客戶先通過委托類的字段得到另一個對象,然后調(diào)用后者的函數(shù),那么客戶必須知曉這一層委托關(guān)系。萬一委托關(guān)系發(fā)生變化,客戶也得相應(yīng)變化。
可以在服務(wù)對象上放置一個簡單的委托函數(shù),將委托關(guān)系隱藏起來,從而去除這種依賴。這么一來,即便將來發(fā)生委托關(guān)系上的關(guān)系,變化也將被限制在服務(wù)對象中,不會涉及客戶。
示例代碼
6.移除中間人
在“隱藏委托關(guān)系”中,談到了“封裝受托對象的好處”。但是這層封裝是需要付出代價的:每當(dāng)客戶要使用受托類的新特性時,你就必須在服務(wù)器端添加一個簡單委托函數(shù)。但是,隨著受托類的特性越來越多,這一過程就會讓你變得痛苦。
這時,服務(wù)類做了過多的簡單委托動作,完全變成了一個“中間人”,應(yīng)該讓客戶直接調(diào)用受托類。
該手法和“隱藏委托關(guān)系”正好相反,正是由于相反,才能夠在實際的應(yīng)用中進(jìn)行靈活的變通。可能一些委托關(guān)系需要保留,而另一些卻需要移除,讓客戶直接使用受托對象。這些都是可以隨之變通的。
7.引入外加函數(shù)
你正在使用一個類,它真的很好,為你提供了需要的所有服務(wù)。
而后,你又需要一項新服務(wù),這個類卻無法供應(yīng)。于是你開始咒罵“為什么不能做這件事?”如果可以修改源碼,你便可以自行添加一個新函數(shù);
若無法修改此類的源代碼(不完美的庫類),此時可以在客戶端建立一個函數(shù),并在第一個參數(shù)傳入服務(wù)類的實例 示例代碼
適配器模式也可完成這個任務(wù),但會增加一個額外的類。
8.引入本地擴(kuò)展
我們都無法預(yù)知一個類的未來,它們常常無法為你預(yù)先準(zhǔn)備一些有用的函數(shù)。如果可以修改源碼,那就太好了,那樣就可以直接加入自己需要的函數(shù)。但是你經(jīng)常無法修改源碼。如果只是需要一兩個函數(shù),可以引入外加函數(shù)進(jìn)行處理。但如果需要多個函數(shù),外加函數(shù)就很難控制它們了。
所以,需要將這些額外函數(shù)組織起來,放到一個恰當(dāng)?shù)牡胤饺ァR_(dá)到這樣的目的,需要用到子類化(適配器模式)和包裝(裝飾器模式)這兩種技術(shù)。這種情況下,把子類或包裝類統(tǒng)稱為本地擴(kuò)展。
三、重新組織數(shù)據(jù)
1.自封裝字段
不要直接訪問類中的成員變量(字段),這會增大耦合。為這些字段創(chuàng)造getter和setter方法,且自能用這些方法訪問字段。
2.以對象取代數(shù)據(jù)值
開發(fā)初期,我們以簡單的數(shù)據(jù)項表示簡單的情況。但是隨著開發(fā)的進(jìn)行,你可能會發(fā)現(xiàn),這些簡單數(shù)據(jù)項不再那么簡單了。比如說,一開始你可能會用一個字符串來表示“電話號碼”概念,但是隨后就會發(fā)現(xiàn),電話號碼需要“格式化”、“抽取區(qū)號”之類的特殊行為。如果這樣的數(shù)據(jù)有一兩個,還可以把相關(guān)函數(shù)放進(jìn)數(shù)據(jù)項所屬的對象里;但是Duplicate Code(重復(fù)代碼)壞味道和Feature Envy(依戀情結(jié))壞味道很快就會從代碼中散發(fā)出來。
當(dāng)這些壞味道開始出現(xiàn),就應(yīng)該將數(shù)據(jù)值變成對象。
3.將值對象改為引用對象
系統(tǒng)中的對象可以分為引用對象和值對象.
引用對象每個對象代表真實世界的一個實物,你可以直接以“==”檢查兩個對象是否相等。
值對象像是“錢”、“日期”這樣的東西,它們完全由其所含的數(shù)據(jù)值來定義,你并不在意副本的存在。值對象有一個非常重要的特性,它們應(yīng)該是不可變的
有時候,你會從一個簡單的值對象開始,在其中保存少量的不可修改的數(shù)據(jù)。而后,你可能會希望給這個對象加入一些可修改數(shù)據(jù),并確保對任何一個對象的修改都能影響到所有引用此一對象的地方,這時候,你就希望將這個對象變?yōu)橐粋€引用對象。
4.將引用對象改為值對象
若一個引用對象很小,且確定其是不可變的,那么可以將其變?yōu)橹祵ο?#xff0c;方便管理。
5.以對象取代數(shù)組
若一個數(shù)組中的各個元素代表不同的東西,那么將這個數(shù)組替換為對象,使用成員變量標(biāo)識數(shù)組的不同元素。
6.復(fù)制“被監(jiān)視的數(shù)據(jù)”
一個分層良好的系統(tǒng),應(yīng)該將處理用戶界面(GUI)和處理業(yè)務(wù)邏輯(Business Logic)的代碼分開。
若一些領(lǐng)域數(shù)據(jù)存在GUI里,而業(yè)務(wù)邏輯函數(shù)需要訪問這些數(shù)據(jù)。此時可以將這些數(shù)據(jù)復(fù)制到一個領(lǐng)域?qū)ο笾?#xff0c;并用觀察者模式同步領(lǐng)域?qū)ο蠛虶UI對象的重復(fù)數(shù)據(jù)。
7.將單向關(guān)聯(lián)改為雙向關(guān)聯(lián)
若兩個類都需要使用對方的屬性,但其中只有一條單向連接。此時可以添加一個方向指針,并使修改雙方關(guān)系的函數(shù)同時更新這兩條連接。
8.將雙向關(guān)聯(lián)改為單向關(guān)聯(lián)
在擁有雙向關(guān)聯(lián)的兩個類間,當(dāng)其中一個類不再需要另一個類的特性使,可以去除不必要的關(guān)聯(lián)。
9.以字面常量取代魔法數(shù)
若一個字面常量帶有特殊含義,此時可以創(chuàng)造一個常量,賦值為字面常量,并使用有意義的變量名。
10.封裝字段
將public字段聲明為private并提供訪問函數(shù)。
11.封裝集合
在函數(shù)返回集合時,讓函數(shù)返回這個集合的只讀副本,并提供修改集合元素的函數(shù)。
示例代碼
12.以數(shù)據(jù)類取代記錄
記錄型結(jié)構(gòu)是許多編程環(huán)境的共同性質(zhì)。有一些理由使它們被帶進(jìn)面向?qū)ο蟪绦蛑?#xff1a;你可能面對的是一個遺留程序,也可能需要通過一個傳統(tǒng)API來與記錄結(jié)構(gòu)交流,或是處理從數(shù)據(jù)庫讀出的記錄。這些時候你就有必要創(chuàng)建一個接口類,用以處理這些外來數(shù)據(jù)。最簡單的做法就是先建立一個看起來類似外部記錄的類,以便日后將某些字段和函數(shù)搬移到這個類中。
新建一個類(“啞”數(shù)據(jù)對象),表示這個記錄。對應(yīng)記錄中的每一項數(shù)據(jù),在新建的類中建立對應(yīng)的一個private字段。并提供相應(yīng)的取值/設(shè)值函數(shù)。
13.以類取代類型🐎
若一個類有一個不影響類行為的類型碼,可以以一個新的類替換這個數(shù)值類型碼。
在使用Replace Type Code with Class (以類取代類型碼)之前,你應(yīng)該先考慮類型碼的其他替換方式。
只有當(dāng)類型碼是純粹數(shù)據(jù)時(也就是類型碼不會在switch語句中引起行為變化時),你才能以類來取代它。
更重要的是:任何switch語句都應(yīng)該運用Replace Conditional with Polymorphism (以多態(tài)取代條件表達(dá)式)去掉。
為了進(jìn)行那樣的重構(gòu),你首先必須運用 Replace Type Code with Subclass (以子類取代類型碼)或Replace Type Code with State/Strategy (以狀態(tài)策略取代類型碼),把類型碼處理掉。
14.以子類取代類型🐎
若一個類有一個會影響類行為的類型碼,可以以一個子類取代這個類型碼。
示例代碼
15.以State(狀態(tài)模式)/Strategy(策略模式)取代類型🐎
若一個類有一個會影響行為的類型碼,但無法通過繼承消除它(類型碼的值在對象生命期中發(fā)生變化或其他原因使得宿主類不能被繼承),此時可以用狀態(tài)對象或具體策略對象取代類型碼。
16.以字段取代子類
若一個類的各個子類間唯一的差別只在“返回常量數(shù)據(jù)”的函數(shù)身上,此時可以修改這些函數(shù),是它們返回超類的某個(新增)字段,然后銷毀子類。
建立子類的目的是為了增加新特性或變化父類行為,如果其帶來的影響只是返回的數(shù)據(jù)值不同,就沒有存在的必要
四、簡化條件表達(dá)式
1.分解條件表達(dá)式
若一個條件表達(dá)式過于復(fù)雜,可以將if-then-else三個段落分別提煉獨立的函數(shù)。
2.合并條件表達(dá)式
若在并列的條件表達(dá)式中,它們的then語句有相同操作,可以用邏輯與和邏輯或語句將各條件中的if語句聯(lián)立,并將then語句合并。
合并后的條件代碼會告訴你“實際上只有一次條件檢查,只不過有多個并列條件需要檢查而已”,從而使這一次檢查的用意更清晰。
其次,這項重構(gòu)往往可以為你使用“提取方法”做好準(zhǔn)備。
3.合并重復(fù)的條件片段
若在條件表達(dá)式的每個分支上有相同的代碼,此時可以將這段重復(fù)代碼搬移到條件表達(dá)式之外。
4.移除控制標(biāo)記
若在一系列布爾表達(dá)式中,某個變量帶有控制標(biāo)記的作用,可以用break、continue和return語句取代控制標(biāo)記。
5.以衛(wèi)語句取代嵌套條件表達(dá)式
條件表達(dá)式通常有2種表現(xiàn)形式。第一:所有分支都屬于正常行為。第二:條件表達(dá)式提供的答案中只有一種是正常行為,其他都是不常見的情況。
這2類條件表達(dá)式有不同的用途。如果2條分支都是正常行為,就應(yīng)該使用形如if……else……的條件表達(dá)式;
如果某個條件極其罕見,就應(yīng)該單獨檢查該條件,并在該條件為真時立刻從函數(shù)中返回。這樣的單獨檢查常常被稱為“衛(wèi)語句”。
給某個分支以特別的重視。它告訴閱讀者:這種情況很罕見,如果它真的發(fā)生了,請做一些必要的整理工作,然后退出。
6.以多態(tài)取代條件表達(dá)式
若一個條件表達(dá)式根據(jù)對象類型的不同而選擇不同的行為,此時應(yīng)將條件表達(dá)式的每個分支拆分為一個子類內(nèi)的override函數(shù)中,然后將原始函數(shù)聲明為抽象函數(shù)。
7.引入Null對象
將null值替換為null對象,這樣就不用在每一個引用處判斷對象是否為空。
示例代碼
8.引入斷言
為一段需要對程序狀態(tài)做出某種假設(shè)的代碼添加斷言,以明確表現(xiàn)這種假設(shè)。
五、 簡化函數(shù)調(diào)用
1.函數(shù)改名
修改那些不能明確揭示函數(shù)用途的函數(shù)名稱。
2.添加參數(shù)
但某個函數(shù)需要從調(diào)用端獲得更多信息時,為這個函數(shù)添加一個對象參數(shù),讓該對象帶進(jìn)函數(shù)所需信息。
除了添加參數(shù)外,你常常還有其他選擇。只要可能,其他選擇都比添加參數(shù)要好,因為它們不會增加參數(shù)列的長度。過長的參數(shù)列是不好的味道,因為程序員很難記住那么多參數(shù)而且長參數(shù)列往往伴隨著壞味道:數(shù)據(jù)泥團(tuán)(Data Clumps)。
3.移除參數(shù)
移除函數(shù)體不再用到的參數(shù)。
4.將查詢函數(shù)和修改函數(shù)分離
將既返回對象狀態(tài)值,又能修改對象狀態(tài)的函數(shù)分離為兩個函數(shù),一個負(fù)責(zé)查詢,一個負(fù)責(zé)修改。
5.令函數(shù)攜帶參數(shù)
若兩個任務(wù)類似的函數(shù),只因少數(shù)幾個值使行為略為不同,這種情況下,可以建立單一的函數(shù),并通過參數(shù)來處理那些變化,用以簡化問題,去除重復(fù)代碼,并提高靈活性。
6.以明確函數(shù)取代參數(shù)
若一個函數(shù)完全取決于參數(shù)值而采取不同的行為,可以針對參數(shù)的每一個可能值建立一個單獨的函數(shù)。
7.保持對象完整
若函數(shù)參數(shù)是某個對象中的若干字段,此時可以改為傳遞整個對象。
8.以函數(shù)取代參數(shù)
如果函數(shù)可以通過其他途徑獲得參數(shù)值,那么它就不應(yīng)該通過參數(shù)取得該值。
如果對象調(diào)用了某個函數(shù),并將這個函數(shù)的返回值作為參數(shù)傳遞給另一個參數(shù),但后者其實可以直接調(diào)用前者,這時應(yīng)讓后者在函數(shù)體中直接調(diào)用前者,并獲取前者返回值,而不需要使用參數(shù)傳遞。
9.引入?yún)?shù)對象
若某些參數(shù)總是同時出現(xiàn),則可以以一個對象取代這些參數(shù)。
10.移除設(shè)值函數(shù)
若類中某個字段只在對象被創(chuàng)建時被設(shè)值,之后不再改變,則應(yīng)去掉該字段的setter函數(shù)
11.隱藏函數(shù)
如果一個函數(shù)從來沒有被其他類用到,那么應(yīng)將這個函數(shù)修改為privete。
12.以工廠函數(shù)取代構(gòu)造函數(shù)。
如果創(chuàng)建對象時不僅僅要做簡單的構(gòu)建動作,那么應(yīng)將構(gòu)造函數(shù)替換為工廠函數(shù)。
13.封裝向下轉(zhuǎn)型
向下轉(zhuǎn)型也許是無法避免的,但你仍然應(yīng)該盡可能少做。如果你的某個函數(shù)返回一個值,并且你知道所返回的對象類型比函數(shù)簽名所昭告的更特化,你便是在函數(shù)用戶身上強(qiáng)加了非必要的工作。這種情況下你不應(yīng)該要求用戶承擔(dān)向下轉(zhuǎn)型的責(zé)任,應(yīng)該盡量為他們提供準(zhǔn)確的類型。
以上所說的情況,常會在返回迭代器或集合的函數(shù)身上發(fā)生。此時你就應(yīng)該觀察人們拿這個迭代器干什么用,然后針對性地提供專用函數(shù)。
將向下轉(zhuǎn)型的動作轉(zhuǎn)移到函數(shù)體中,而不是讓用戶完成這個動作。
14.以異常取代錯誤碼
對于一個成員方法而言,它要么執(zhí)行成功,要么執(zhí)行失敗。成員方法執(zhí)行成功的情況很容易理解,但如果執(zhí)行失敗了卻沒有那么簡單,因為我們需要將執(zhí)行失敗的原因通知調(diào)用者。拋出異常和返回錯誤代碼都是用來通知調(diào)用者的手段
當(dāng)我們想要告示調(diào)用者更多細(xì)節(jié)的時候,就需要與調(diào)用者約定更多的錯誤代碼。于是。錯誤代碼飛速膨脹,直到看起來似乎無法維護(hù),因為我們需要查找并確認(rèn)錯誤代碼。
使用了CLR異常機(jī)制后,代碼更加清晰,易于理解。
而在某些情況下,錯誤代碼將無用武之地,如構(gòu)造函數(shù)、操作符重載及屬性。語法的特性決定了其不具備任何返回值,于是異常處理被當(dāng)做取代錯誤代碼的首要選擇。
將函數(shù)返回錯誤碼改為使用異常。
示例代碼
15.以測試取代異常
異常只應(yīng)該被用于異常的,罕見的行為,也就是那些產(chǎn)生意料之外錯誤的行為,而不應(yīng)該成為條件檢查的替代品
如果調(diào)用者有能力在異常被拋出之前檢查拋出條件,那么應(yīng)該讓調(diào)用者在調(diào)用函數(shù)前檢查條件。
示例代碼
六、處理概括關(guān)系
1.字段上移
當(dāng)兩個子類擁有相同字段時,將該字段移至超類。
2.函數(shù)上移
當(dāng)函數(shù)在各個子類產(chǎn)生完全相同的結(jié)果時,將該函數(shù)移至超類。
3.構(gòu)造函數(shù)本體上移
當(dāng)各個子類中的構(gòu)造函數(shù)幾乎完全移至?xí)r,在超類新建一個構(gòu)造函數(shù),并在子類構(gòu)造函數(shù)中調(diào)用它。
4.函數(shù)下移
當(dāng)超類的某個函數(shù)只與部分子類有關(guān)時,將這個函數(shù)移到相關(guān)子類中。
5.字段下移
當(dāng)超類中的某個字段只被部分子類用到時,將這個字段移到需要它的子類中。
6.提煉子類
當(dāng)類中某些特性只被部分實例用到時,新建一個子類,將那部分特性移到子類中。
Extract Class(提煉類)是Extract Subclass之外的另一種選擇,兩者之間的抉擇其實就是委托和繼承之間的抉擇。Extract Subclass通常更容易進(jìn)行,但它也有限制:一旦對象創(chuàng)建完成,你無法再改變與類型相關(guān)的行為。此外,子類只能用以表現(xiàn)一組變化。如果你希望一個類以幾種不同的方式變化,就必須使用委托。
7.提煉超類
當(dāng)兩個類有相似特性時,為這兩個類建立一個超類,將相同特性移至超類中。
8.提煉接口
如果若干用戶使用類中相同的特殊方法,或兩個類的方法有部分相同,可以將相同方法提煉到一個接口中,并讓這些類實現(xiàn)接口。
實例代碼
9.折疊繼承體系
如果超類和子類沒有太大區(qū)別,那么將他們合為一體。
10.塑造模板函數(shù)。
如果一些子類中相應(yīng)的某些函數(shù)以相同順序執(zhí)行類似的操作,但操作細(xì)節(jié)在不同子類間有所不同。這時可以將子類中對應(yīng)函數(shù)修改為相同的函數(shù)名,并在超類中創(chuàng)建對應(yīng)的被重寫的函數(shù),最后在超類中創(chuàng)建一個新的函數(shù),按順序執(zhí)行這些函數(shù)完成操作。
實例代碼
11.以委托取代繼承
如果某個子類只是用了超類的一部分接口,或者沒有使用超類的數(shù)據(jù),那么應(yīng)去除繼承關(guān)系,轉(zhuǎn)而在子類中新建一個持有超類的字段,并調(diào)整子類函數(shù),改為委托超類。
代碼示例
12.以繼承取代委托
如果在兩個類中使用委托關(guān)系會導(dǎo)致編寫許多極簡單的委托函數(shù),這時應(yīng)考慮讓委托類繼承受托類。
七、大型重構(gòu)
1.梳理并分解繼承體系
如果某個繼承體系同時承擔(dān)兩項責(zé)任,這時應(yīng)建立兩個繼承體系,并通過委托關(guān)系使其中一個調(diào)用另一個。
2.將過程化設(shè)計轉(zhuǎn)化為對象設(shè)計
針對以傳統(tǒng)過程化分隔編寫的代碼,將它的數(shù)據(jù)記錄變成對象,將大塊行為分成小塊并移入相關(guān)對象中。
3.將領(lǐng)域和表述/顯示分離
如果某些GUI類包含了領(lǐng)域邏輯,那么應(yīng)將領(lǐng)域邏輯分離出來,建立單獨的領(lǐng)域類。
4.提煉繼承體系
如果某個類做了太多的、以大量條件表達(dá)式完成的工作,那么應(yīng)建立繼承體系,以一個子類表示其中的一種特殊情況。
總結(jié)
以上是生活随笔為你收集整理的软件配置管理(五)常用重构技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件配置管理(四)代码味道与重构
- 下一篇: 软件项目组织管理(一)项目管理概述