在對對象的設(shè)計過程中栈暇,“決定把責(zé)任放在哪兒”即使不是最重要的事馆蠕,也是最重要的事情之一涝桅。
1 Move Method
<b>在該函數(shù)最常引用的類中建立一個有著類似行為的新函數(shù)闷游,將舊函數(shù)變成一個單純的委托函數(shù),或是將舊函數(shù)完全移除贴汪。</b>
<b>Motivation:</b>函數(shù)遷移是重構(gòu)理論的支柱脐往。如果一個類有太多行為,或如果一個類和另一個類有太多合作而形成高度耦合扳埂,就需要搬移函數(shù)业簿。一般在如下情況下需要搬移函數(shù):使用另一個對象的次數(shù)比使用自己所駐對象的次數(shù)還多。
<b>Routinue:</b>
- 檢查源類中被源函數(shù)所使用的一切特性阳懂,考慮它們是否需要被搬移梅尤。
- 檢查源類的子類和超類,看看是否有該函數(shù)的其他聲明岩调。
- 在目標(biāo)函數(shù)中聲明這個函數(shù)巷燥。
- 將源函數(shù)的代碼復(fù)制到目標(biāo)函數(shù)中。調(diào)整以保證可運(yùn)行号枕。
- 編譯目標(biāo)類缰揪。
- 決定如何從源函數(shù)正確引用目標(biāo)對象。
- 修改源函數(shù)葱淳,使他成為一個委托函數(shù)钝腺。
- 編譯抛姑,測試。
- 決定是否刪除源函數(shù)艳狐,或?qū)⑺鳛橐粋€委托函數(shù)保留下來定硝。
- 如果要移除源函數(shù),請將源類中對源函數(shù)的所有調(diào)用毫目,替換為對目標(biāo)函數(shù)的調(diào)用蔬啡。
- 編譯,測試蒜茴。
2 Move Field
<b>在目標(biāo)類新建一個字段星爪,修改源字段的所有用戶,令它們改用新字段粉私。</b>
Motivation:如果一個字段顽腾,在其所駐類之外的另一個類中有更多函數(shù)使用它,就會考慮搬移這個字段诺核。
Routinue:
- 如果字段的訪問級是public抄肖,使用Encapsulate Field將它封裝起來。
- 編譯測試窖杀。
- 在目標(biāo)類中建立與源相同的字段漓摩,并同時建立相應(yīng)的設(shè)值/取值函數(shù)。
- 編譯目標(biāo)類入客。
- 決定如何在源對象中引用目標(biāo)對象管毙。
- 刪除原字段。
- 將所有對原字段的引用替換為對某個目標(biāo)函數(shù)的調(diào)用桌硫。
- 編譯夭咬,測試。
3 Extract Class
建立一個新類铆隘,將相關(guān)的字段和函數(shù)從舊類搬移到新類卓舵。
Motivation:一個類應(yīng)該是一個清楚的抽象,處理一些明確的責(zé)任膀钠。這時候就需要切分類出子類掏湾。
Routinue:
- 決定如何分解類所負(fù)的責(zé)任。
- 建立一個新類肿嘲,用以表現(xiàn)從舊類中分離出來的責(zé)任融击。
- 建立“從舊類訪問新類”的連接關(guān)系。
- 對于每一個想要搬移的字段雳窟,使用Move Field搬移砚嘴。
- 每次搬移后,編譯、測試际长。
- 使用Move Method將必要函數(shù)搬移到新類耸采。先搬移較低層次函數(shù)(被調(diào)用多的函數(shù)),再搬移較高層次函數(shù)工育。
- 每次搬移之后虾宇,編譯、測試如绸。
- 檢查嘱朽,精簡每個類的接口。
- 決定是否公開新類怔接。如果你的確需要公開它搪泳,就要決定讓它成為引用對象還是不可變的值對象。
4 Inline Class
將一個類的所有特性移動到另一個類中扼脐,并移除源類岸军。
Motivation:與Extract Class相反。如果一個類不再承擔(dān)足夠的責(zé)任瓦侮、不再有單獨(dú)存在的理由艰赞,將之融入另一個類。
5 Hide Delegate(隱藏“委托關(guān)系”)
在服務(wù)類上建立客戶所需的所有函數(shù)肚吏,用以隱藏委托關(guān)系方妖。
Motivation:為了更好地實現(xiàn)封裝特性,如果客戶通過服務(wù)對象獲得另一個對象罚攀,這就導(dǎo)致了客戶必須知道這種委托關(guān)系党觅,那么當(dāng)委托關(guān)系發(fā)生關(guān)系了,客戶也要相應(yīng)變化斋泄。將委托關(guān)系隱藏起來杯瞻,從而可以去除這種依賴。
Routinue:
- 對于每一個委托關(guān)系的函數(shù)是己,在服務(wù)對象端建立一個簡單的委托函數(shù)又兵。
- 調(diào)整客戶任柜,令它只調(diào)用服務(wù)對象提供的函數(shù)卒废。
- 每次調(diào)整后,編譯并測試宙地。
- 如果獎勵啊不再有任何客戶需要摔认,變移除服務(wù)對象中的相關(guān)訪問函數(shù)。
- 編譯宅粥,測試参袱。
6 Remove Middle Man(移動中間人)
讓客戶直接調(diào)用委托類。
Motivation:委托的代價是,委托對象的變化會引起服務(wù)器對象變化抹蚀。當(dāng)服務(wù)類完成成了委托類的中間人剿牺,那么請刪除中間人吧!Don't Be Afraid环壤!不斷使用Hide Delegate和Move Middle Man調(diào)整程序結(jié)構(gòu)晒来。
Routinue:
- 建立函數(shù),用以獲得受托對象郑现。
- 對于每個委托函數(shù)湃崩,在服務(wù)類中刪除該函數(shù),并讓需要調(diào)用該函數(shù)的客戶轉(zhuǎn)為調(diào)用受托對象接箫。
- 處理每個委托函數(shù)后攒读,編譯、測試辛友。
7 Introduce Foreign Method(引入外加函數(shù))
在客戶類中建立一個函數(shù)薄扁,并以第一參數(shù)形式傳入一個服務(wù)類實例。
Motivation:根據(jù)單一職責(zé)原則瞎领,好的編程框架泌辫,往往類的職責(zé)是單一的,那么當(dāng)需要其他的功能的時候九默。重復(fù)代碼是萬惡之源震放!不應(yīng)該無線外加函數(shù)。
Routinue:
- 在客戶類中建立一個函數(shù)驼修,用來提供你需要的功能殿遂。
- 以服務(wù)類實例作為該函數(shù)的第一個參數(shù)。
- 將該函數(shù)注釋為:“外加函數(shù)乙各,應(yīng)在服務(wù)類實現(xiàn)墨礁。”
8 Introduce Local Extension(引入本地擴(kuò)展)
建立一個新類耳峦,使它包含這些額外函數(shù)恩静。讓這個擴(kuò)展品成為源類的子類或包裝類。
Motivation:類作者蹲坷,包括自己都無法預(yù)知未來驶乾。所以往往沒有需要的接口。當(dāng)然我們可以使用Foreign Method循签,然如果需要的額外函數(shù)過多级乐,外加函數(shù)就很難控制了。這時候需要將函數(shù)組織起來县匠,有兩種標(biāo)準(zhǔn)技術(shù)——子類化和包裝风科。這成為本地擴(kuò)展撒轮。也就是在原來的基礎(chǔ)上加入新的特性。
- 建立一個擴(kuò)展類贼穆,將它作為原始類的子類或包裝類题山。
- 在擴(kuò)展類中加入轉(zhuǎn)型構(gòu)造函數(shù)。即以源對象為參數(shù)的構(gòu)造函數(shù)故痊。如果用的是子類化方案臀蛛,則應(yīng)該調(diào)用超類構(gòu)造函數(shù);如果是包裝的方案崖蜜,則應(yīng)該作為實例保存起來浊仆,用作接收委托的源對象。
- 在擴(kuò)展類中加入新特性豫领。
- 根據(jù)需要抡柿,將原對象替換為擴(kuò)展對象。
- 將針對原始類定義的所有外加函數(shù)搬移到擴(kuò)展類中等恐。