0. 本章內(nèi)容導(dǎo)圖
本章所介紹的重構(gòu)手法專門用來處理類的概括關(guān)系(generalization,即繼承關(guān)系)。
1. 重構(gòu)手法
1.1 字段上移
概要:
兩個子類擁有相同的字段。
將該字段移至超類。
動機(jī):
a. 處理兄弟類間重復(fù)的字段以及因重復(fù)字段造成的函數(shù)重復(fù)
示例圖:
總結(jié):
? ? 如果子類是分別開發(fā)的扔罪,或者是在重構(gòu)過程中組合起來的,往往會擁有重復(fù)性桶雀,尤其是字段重復(fù)矿酵。命名并非判斷字段是否重復(fù)的標(biāo)準(zhǔn),要依據(jù)它們是如何被方法使用的矗积。
1.2 函數(shù)上移
概要:
有些函數(shù)全肮,在各個子類中產(chǎn)生完全相同的結(jié)果。
將該函數(shù)移至超類棘捣。
動機(jī):
a. 避免行為重復(fù)
示例圖:
總結(jié):
? ? 重復(fù)是滋生bug的溫床辜腺,任何時候,只要系統(tǒng)內(nèi)出現(xiàn)重復(fù),都應(yīng)該設(shè)法去除重復(fù)哪自。相較于“提煉函數(shù)”丰包,”函數(shù)上移“特指出現(xiàn)在兄弟類之間的函數(shù)重復(fù)。
1.3 構(gòu)造函數(shù)本體上移
概要:
你在各個子類中擁有一些構(gòu)造函數(shù)壤巷,它們的本體幾乎完全一致邑彪。
在超類中新建一個構(gòu)造函數(shù),并在子類構(gòu)造函數(shù)中調(diào)用它胧华。
動機(jī):
a. 處理對象構(gòu)建過程中的代碼重復(fù)
示例:
重構(gòu)前:
class Manager extends Employee {
public Manager(String name, String id, int grade) {
mName = name;
mId = id;
mGrade = grade;
}
}
重構(gòu)后:
public Manager(String name, String id, int grade) {
super(name, id);
mGrade = grade;
}
總結(jié):
? ? 構(gòu)造函數(shù)不是普通的函數(shù)寄症,比使用普通函數(shù)有更多的限制,因此對于構(gòu)造函數(shù)中涉及的重復(fù)要同普通函數(shù)中的重復(fù)區(qū)別開來矩动。
1.4 函數(shù)下移
概要:
超類中的某個函數(shù)只與部分(而非全部)子類有關(guān)有巧。
將這個函數(shù)移到相關(guān)的那些子類去。
動機(jī):
a. 超類中的存在應(yīng)該是所有子類的共同抽象悲没,與子類有關(guān)的函數(shù)應(yīng)在子類中處理
示例圖:
總結(jié):
? ? 超類中的字段和接口應(yīng)該是所有子類共同擁有的特征篮迎,不應(yīng)將對某些子類的特殊處理放在超類中進(jìn)行。這也是對依賴倒轉(zhuǎn)原則的遵守示姿。
依賴倒轉(zhuǎn)原則:抽象不應(yīng)該依賴于細(xì)節(jié)甜橱,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象。換句話說就是栈戳,要針對接口編程岂傲,而非針對實(shí)現(xiàn)編程。
1.5 字段下移
概要:
超類中的某個字段只被部分(而非全部)子類用到子檀。
將這個字段移到需要它的那些子類去镊掖。
動機(jī):
a. 超類中的存在應(yīng)該是所有子類的共同抽象,與子類有關(guān)的字段應(yīng)在子類中處理
示例圖:
總結(jié):
? ? 超類應(yīng)該是所有子類都具有的共同特征的抽象褂痰。
1.6 提煉子類
概要:
類中的某些特性只被某些(而非全部)實(shí)例用到亩进。
新建一個子類,將上面所說的那一部分特性移到子類中脐恩。
動機(jī):
a. 類中的某些行為只被一部分實(shí)例用到镐侯,其他實(shí)例不需要它們
示例圖:
總結(jié):
? ? 類是對象的藍(lán)圖,類定義了使用該類創(chuàng)建的所有對象具有的屬性和行為驶冒。如果類中的某些特性不是全部實(shí)例都具備的苟翻,就應(yīng)該將那些部分實(shí)例才具有的特性泛化到子類中。
泛化:一般事物(稱為超類或父類)和該事物的較為特殊的種類(稱為子類)之間的關(guān)系骗污。
1.7 提煉超類
概要:
兩個類有相似特性崇猫。
為這兩個類建立一個超類,將相同特性移至超類需忿。
動機(jī):
a. 消除系統(tǒng)中兩個相似類中的重復(fù)诅炉,建立抽象體系
示例圖:
總結(jié):
? ? 有很多因素會導(dǎo)致系統(tǒng)中出現(xiàn)共通性的類蜡歹,沒有為它們建立出繼承結(jié)構(gòu),此重構(gòu)手法就是針對這種情況涕烧,幫助找出共通性月而,建立繼承結(jié)構(gòu)。這是一種抽象的過程议纯。
抽象關(guān)注一個對象的外部視圖父款,可以用來分離對象的基本行為和它的實(shí)現(xiàn)。
1.8 提煉接口
概要:
若干客戶使用類接口中的同一子集瞻凤,或者兩個類的接口有部分相同憨攒。
將相同的子集提煉到一個獨(dú)立接口中。
動機(jī):
a. 針對接口編程
b. 接口隔離阀参,分離責(zé)任
示例圖:
總結(jié):
? ? 針對客戶的使用分離責(zé)任可以使系統(tǒng)的用法更加清晰肝集,同時也容易看清系統(tǒng)的責(zé)任劃分。你可以從同一個類建立多個不同的接口蛛壳,以滿足多個客戶不同的需要杏瞻。這也是接口隔離原則要求的實(shí)現(xiàn)方式。
接口隔離原則:使用多個專門的接口衙荐,而不使用單一的總接口伐憾,即客戶端不應(yīng)該依賴那些它不需要的接口。
1.9 折疊繼承體系
概要:
超類和子類之間無太大區(qū)別赫模。
將它們合為一體。
動機(jī):
a. 消除不必要的繼承關(guān)系
示例圖:
總結(jié):
? ? 系統(tǒng)中的每個類都是需要維護(hù)的蒸矛,當(dāng)發(fā)現(xiàn)系統(tǒng)中有這種設(shè)計(jì)或者由于其他重構(gòu)出現(xiàn)了這種情況瀑罗,就需要果斷處理。
1.10 塑造模板函數(shù)
概要:
你有一些子類雏掠,其中相應(yīng)的某些函數(shù)以相同順序
執(zhí)行類似的操作斩祭,但各個操作的細(xì)節(jié)上有所不同
。
將這些操作分別放進(jìn)獨(dú)立函數(shù)中乡话,并保持它們都有相同的簽名摧玫,于是原函數(shù)也就變得相同了。然后將原函數(shù)上移至超類绑青。
動機(jī):
a. 消除重復(fù)诬像,分離不變性和可變性
示例圖:
總結(jié):
? ? 這是通過重構(gòu)實(shí)現(xiàn)模板方法模式的過程。
模板方法模式:定義一個操作中算法的框架闸婴,而將一些步驟延遲到子類中坏挠。模板方法模式使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
1.11 以委托取代繼承
概要:
某個子類只使用超類接口中的一部分邪乍,或是根本不需要繼承而來的數(shù)據(jù)降狠。
在子類中新建一個字段用以保存超類对竣;調(diào)整子類函數(shù),令它改而委托超類榜配;然后去掉兩者之間的繼承關(guān)系否纬。
動機(jī):
a. 消除誤用的泛化關(guān)系
示例圖:
總結(jié):
? ? 對于繼承關(guān)系,你需要理解父類和子類之間應(yīng)該是一種is-a的關(guān)系蛋褥,如果僅僅是因?yàn)榇a復(fù)用而采用繼承是大錯特錯的临燃。繼承和組合都是實(shí)現(xiàn)代碼復(fù)用的方式,使用它們之前壁拉,要先弄明白類之間的關(guān)系谬俄。
1.12 以繼承取代委托
概要:
你在兩個類之間使用委托關(guān)系,并經(jīng)常為整個接口
編寫許多極簡單的委托函數(shù)弃理。
讓委托類繼承受托類溃论。
動機(jī):
a. 需要使用受托類中的所有函數(shù),且所有委托函數(shù)都是極簡單的
示例圖:
總結(jié):
? ? 要注意此重構(gòu)手法使用的條件痘昌,即委托類為受托類的所有接口
都編寫了極簡單
的委托函數(shù)钥勋。合成復(fù)用原則告訴我們,在涉及代碼復(fù)用時辆苔,要優(yōu)先考慮組合算灸,而非繼承。但當(dāng)對象之間是一種泛化的關(guān)系時驻啤,就要考慮將依賴關(guān)系改為泛化關(guān)系菲驴。
? ? 如果受托對象被不止一個其他對象共享,而且受托對象是可變的骑冗,就不能將委托關(guān)系替換為繼承關(guān)系赊瞬,因?yàn)椴荒苡绊懙狡渌麑ο髮υ撌芡袑ο蟮氖褂眉皵?shù)據(jù)共享。
合成復(fù)用原則:盡量使用對象組合贼涩,而不是繼承來達(dá)到復(fù)用的目的巧涧。即復(fù)用時要優(yōu)先考慮組合,而非繼承遥倦。