《重構(gòu)》讀書筆記

《重構(gòu)》讀書筆記

總覽

第一部分

第一章從實(shí)例程序出發(fā),展示設(shè)計(jì)的缺陷,對(duì)其重構(gòu)可以了解重構(gòu)的過(guò)程和方法济似。

第二部分

第二章討論重構(gòu)的一般性原則猖凛、定義和進(jìn)行重構(gòu)的原因赂蠢。主要講述概念性的東西。

第三部分

第三章介紹如何嗅出代碼的“壞味道”以及如何用重構(gòu)對(duì)其消除辨泳。

第四部分

第四章講述構(gòu)建java的測(cè)試環(huán)境

第五部分

第五章到第十二章介紹作者整理下來(lái)重構(gòu)的方法虱岂。

第六部分

第十三章重構(gòu)技術(shù)在商業(yè)化應(yīng)用中出現(xiàn)的問(wèn)題

第1章 重構(gòu)玖院,第一個(gè)案例1

1.1 起點(diǎn)1

如果發(fā)現(xiàn)需要為程序添加新特性,而代碼結(jié)構(gòu)使你無(wú)法很方便的達(dá)到目的第岖,那就需要先進(jìn)行重構(gòu)难菌,然后使新特性的添加容易進(jìn)行,再添加新特性蔑滓。

1.2 重構(gòu)的第一步7

首先檢查自己是否有一套可靠的測(cè)試機(jī)制郊酒。這些測(cè)試必須有自我檢測(cè)的能力。因?yàn)橹貥?gòu)有可能引入bug键袱。

1.3 分解并重組statement()8

重構(gòu)技術(shù)就是以 微小的步伐修改程序燎窘,如果發(fā)現(xiàn)錯(cuò)誤,很容易可以發(fā)現(xiàn)它蹄咖。
優(yōu)秀的程序員應(yīng)該寫出人類容易理解的代碼褐健,而非僅僅是計(jì)算機(jī)能理解的代碼。

  • 搬移金額計(jì)算代碼(Move Method)
  • 去除不必要的臨時(shí)變量(Replace Temp with Query)
  • 提煉忱教溃客積分計(jì)算(Move Method)
  • Move Method 如果一個(gè)方法運(yùn)用目標(biāo)對(duì)象的屬性進(jìn)行計(jì)算蚜迅,那么請(qǐng)把這個(gè)方法抽象到目標(biāo)對(duì)象的類中。

1.4 運(yùn)用多態(tài)取代與價(jià)格相關(guān)的條件邏輯34

  • 最好不要在另一個(gè)對(duì)象的屬性基礎(chǔ)上運(yùn)用switch語(yǔ)句俊抵。如果不得不使用谁不,也應(yīng)該在對(duì)象自己的數(shù)據(jù)上使用,而不是別人的數(shù)據(jù)上徽诲。
  • 當(dāng)一個(gè)類需要運(yùn)用多態(tài)的時(shí)候刹帕,但是假如其在生命周期內(nèi)有可能變成另一個(gè)同胞類,那么就應(yīng)該使用state設(shè)計(jì)模式來(lái)解決這個(gè)問(wèn)題馏段。

1.5 結(jié)語(yǔ)52

第2章 重構(gòu)原則53

2.1 何謂重構(gòu)53

  • 名詞含義:對(duì)軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整轩拨,目的是在不改變軟件可觀察行為的前提下,提高理解性和降低修改成本院喜。
  • 動(dòng)詞含義
    使用一系列重構(gòu)手法亡蓉,在不改變軟件可觀察行為的前提下,調(diào)整其結(jié)構(gòu)喷舀。
  • 重構(gòu)讓軟件更容易理解和修改
  • 重構(gòu)不會(huì)改變軟件的可觀察行為砍濒,即使改變也只能是微小的影響,軟件功能一如既往硫麻。
  • 兩頂 帽子(時(shí)間分配)
    • 添加新功能:不應(yīng)該修改已有代碼爸邢,只關(guān)注新功能。增加新測(cè)試拿愧,通過(guò)測(cè)試衡量工作進(jìn)度
    • 重構(gòu):只改變程序內(nèi)部結(jié)構(gòu)杠河,不應(yīng)該添加測(cè)試(存在遺漏),不修改測(cè)試(除非接口發(fā)生變化)
    • 軟件開(kāi)發(fā)在這兩者之間切換

2.2 為何重構(gòu)55

  • 改進(jìn)軟件設(shè)計(jì):

    • 程序的設(shè)計(jì)在沒(méi)有重構(gòu)的情況下逐漸腐敗變質(zhì),功能的增加或者修改可能使代碼越來(lái)越難以理解券敌,就越難保護(hù)其中的設(shè)計(jì)
    • 消除重復(fù)的代碼一方面是程序運(yùn)行更快唾戚,一方面是方便未來(lái)的修改,只用在一處修改即可不用修改多處待诅。
  • 軟件更容易理解:

    • 及時(shí)填補(bǔ)“想要它做什么”和“告訴它做什么”之間的縫隙叹坦。重構(gòu)的核心就是要“準(zhǔn)確說(shuō)出我所要的”
    • 重新閱讀代碼的人有可能是自己,他人卑雁。
    • 通過(guò)重構(gòu)可以把不熟悉的代碼的用途理一遍募书,加深對(duì)代碼的理解
  • 幫助找出bug:這個(gè)是建立在代碼容易理解之上的

  • 提高編程速度:重構(gòu)達(dá)到良好的設(shè)計(jì),而良好的設(shè)計(jì)更容易修改测蹲,增加功能莹捡,調(diào)試。

2.3 何時(shí)重構(gòu)57

  • 三次法則:第一次的時(shí)候做某事盡管去做扣甲。第二次的時(shí)候?qū)λa(chǎn)生反感道盏,還是繼續(xù)去做。第三次再做類似的時(shí)候文捶,就應(yīng)該重構(gòu)了。
  • 添加功能時(shí)重構(gòu):一方面可能是需要理解需要修改的代碼媒咳,另一方面是使增加新特性更加容易粹排。
  • 修補(bǔ)錯(cuò)誤時(shí)重構(gòu):出現(xiàn)bug的時(shí)候,難以找出問(wèn)題所在的時(shí)候涩澡,很有可能是代碼不清晰導(dǎo)致查找bug的困難顽耳。
  • 復(fù)審代碼時(shí)重構(gòu):
    • 復(fù)審代碼有助于知識(shí)的傳播,有利于代碼被編寫者之外的人理解妙同。
    • 重構(gòu)是有利于增強(qiáng)復(fù)審代碼的能力射富,重構(gòu)需要先閱讀代碼得到一定程度的理解,得到一些建議粥帚,然后動(dòng)手實(shí)現(xiàn)胰耗。所以重構(gòu)有利于知道合理的代碼應(yīng)當(dāng)是怎么樣的。
    • 復(fù)審團(tuán)隊(duì)需要精煉芒涡,就 一個(gè)審查者和一個(gè)原作者柴灯。較大的項(xiàng)目可以通過(guò) UML圖去展示代碼的邏輯。
  • 程序難以相與的原因:
    • 難以閱讀的程序费尽,難以修改
    • 邏輯重復(fù)的程序赠群,難以修改
    • 添加新特性需要修改已有代碼的程序,難以修改
    • 帶復(fù)雜邏輯判斷的程序旱幼,難以修改
  • 對(duì)應(yīng)的期望:
    • 容易閱讀
    • 所有邏輯都只有唯一地點(diǎn)指定
    • 新的改動(dòng)不會(huì)危及現(xiàn)有行為
    • 盡可能簡(jiǎn)單表達(dá)邏輯

2.4 怎么對(duì)經(jīng)理說(shuō)60

  • 不要告訴經(jīng)理:經(jīng)理是進(jìn)度驅(qū)動(dòng)查描,就是要求開(kāi)發(fā)者盡快完成任務(wù)。而對(duì)于我來(lái)說(shuō)最快完成任務(wù)的方式就是先重構(gòu)喝滞。
  • 很多時(shí)候重構(gòu)都為程序引入間接層翁垂。把大型對(duì)象拆分成小對(duì)象,把大型函數(shù)拆分為小型函數(shù)蓉冈。
    • 允許邏輯共享:一個(gè)函數(shù)在不同地點(diǎn)被調(diào)用长豁。子類共享超類的方法钧唐。
    • 分開(kāi)解釋意圖和實(shí)現(xiàn):通過(guò)類名和函數(shù)名解釋自己的意圖
    • 隔離變化:在不同地方使用同一個(gè)對(duì)象,需要修改一處邏輯匠襟,那么可以做出子類钝侠,并在需要的時(shí)候修改這個(gè)子類。
    • 封裝條件邏輯:運(yùn)用多態(tài)酸舍。將條件邏輯轉(zhuǎn)化為消息模式帅韧。
  • 減少間接層:當(dāng)間接層只在一處使用,那么需要將其消除啃勉。

2.5 重構(gòu)的難題62

  • 數(shù)據(jù)庫(kù):
    • 程序與數(shù)據(jù)庫(kù)耦合在一起忽舟。另一方面是數(shù)據(jù)遷移,是向繁瑣的事項(xiàng)淮阐。
    • 在非關(guān)系型數(shù)據(jù)庫(kù)叮阅,可以在數(shù)據(jù)庫(kù)和對(duì)象模型中插入一個(gè)分離層,隔離兩者之間的變化
  • 修改接口
    • 對(duì)于已經(jīng)發(fā)布的接口需要可能需要維護(hù)舊接口和新接口泣特,用deprecated修飾舊接口浩姥。
    • 不發(fā)布新接口,在舊接口中調(diào)用新接口状您。
    • 假如新接口拋出編譯時(shí)異常勒叠,那么可以在舊接口中調(diào)用新接口并將編譯時(shí)異常轉(zhuǎn)化為運(yùn)行時(shí)異常。
  • 何時(shí)不重構(gòu)
    • 重構(gòu)之前膏孟,代碼必須能夠在大部分情況下 正常運(yùn)行眯分,不然就不應(yīng)該重構(gòu),而應(yīng)該是 重寫柒桑。
    • 到了Deadline弊决,應(yīng)該避免重構(gòu)。

2.6 重構(gòu)與設(shè)計(jì)66

  • 重構(gòu)與設(shè)計(jì)是彼此互補(bǔ)的幕垦。
  • 預(yù)先設(shè)計(jì)是必須丢氢,預(yù)先設(shè)計(jì)不可能做到完全正確,隨著對(duì)問(wèn)題的逐漸深入先改,通過(guò)重構(gòu)可以改善程序的質(zhì)量疚察。
  • 重構(gòu)減輕了設(shè)計(jì)的難度和壓力,在程序不斷修改的過(guò)程逐步完善程序的設(shè)計(jì)仇奶。

2.7 重構(gòu)與性能69

  • 重構(gòu)是有可能導(dǎo)致程序運(yùn)行變慢的貌嫡。
  • 除了對(duì)實(shí)時(shí)有嚴(yán)格要求的程序比驻,編寫快速軟件的秘訣是:首先寫出可調(diào)的程序,然后調(diào)整它以達(dá)到足夠的速度岛抄。
  • 經(jīng)過(guò)分析大部分程序的大半部分時(shí)間是運(yùn)行在一小半代碼上别惦,所以對(duì)所有代碼一視同仁是錯(cuò)誤的。
  • 性能優(yōu)化放在開(kāi)發(fā)的后期夫椭,通過(guò)分析工具找出消耗大量時(shí)間空間的地方掸掸,然后集中精力優(yōu)化這些地方。

2.8 重構(gòu)起源何處71

第3章 代碼的壞味道75

3.1 DuplicatedCode(重復(fù)代碼)76

  • 同個(gè)類兩個(gè)函數(shù)存在相同表達(dá)式:ExtractMethod(提煉函數(shù))
  • 互為兄弟類內(nèi)存在相同表達(dá)式:
    • ExtractMethod-》PullUpMethod(函數(shù)上移)
    • 如果代碼只是相似:先運(yùn)用ExtractMethod(提煉函數(shù))分開(kāi)再Form TemPlate Method(塑造模板函數(shù))
  • 兩個(gè)毫不相干的類存在重復(fù)代碼:ExtractClass(提煉類)

3.2 LongMethod(過(guò)長(zhǎng)函數(shù))76

  • 原則:每當(dāng)感覺(jué)需要以注釋來(lái)說(shuō)明什么的時(shí)候蹭秋,我就將需要說(shuō)明的代碼放到一個(gè)獨(dú)立的函數(shù)里面
  • 只要函數(shù)名稱能夠 解釋用途扰付,我們就應(yīng)該毫不猶豫地做。
  • 關(guān)鍵不在函數(shù)的長(zhǎng)度仁讨,而在于函數(shù)“做什么”和“如何做”之間的語(yǔ)義距離羽莺。
  • 具體情況
    • 函數(shù)有大量參數(shù)和臨時(shí)變量:ExtractMethod(提煉函數(shù))
    • 用ReplaceTempwithQuery(以查詢?nèi)〈R時(shí)變量)消除臨時(shí)變量
    • 用IntroduceParameterObject(引入?yún)?shù)對(duì)象)或者PreserveWholeObject(保持對(duì)象完整)來(lái)將多長(zhǎng)的參數(shù)列表變得簡(jiǎn)潔一點(diǎn)。
    • 如果按照上述步驟還存在太多變量和參數(shù)就需要用到ReplaceMethodwithMethodObject(以函數(shù)對(duì)象取代函數(shù))
    • 條件表達(dá)式可以用DecomposeConditional(分解條件表達(dá)式)解決
    • 可以將循環(huán)內(nèi)的代碼提煉為函數(shù)洞豁。

3.3 LargeClass(過(guò)大的類)78

  • 有時(shí)候類并非在所有時(shí)刻都使用實(shí)例變量:使用ExtractMethod和ExtractSubclass(提煉子類)
  • 類中有太多代碼:ExtractClass(提煉類)ExtractSubclass(提煉子類)盐固,甚至可以使用提煉接口的方式分解類的行為。
  • 存在GUI的時(shí)候丈挟,可以DuplicateObservedData(復(fù)制“被監(jiān)視數(shù)據(jù)”)刁卜,分離數(shù)據(jù)和行為到領(lǐng)域模型中去。

3.4 LongParameterList(過(guò)長(zhǎng)參數(shù)列)78

  • 如果可以調(diào)用已有對(duì)象獲取的話可以使用ReplaceParameterwithMethods(以函數(shù)取代參數(shù))
  • 將來(lái)自同一對(duì)象的數(shù)據(jù)收集起來(lái)曙咽,以該對(duì)象替代:PreserveWholeObject(保持對(duì)象完整)
  • 如果幾個(gè)參數(shù)總是同時(shí)出現(xiàn)长酗,那么可以考慮IntroduceParameterObject(引入?yún)?shù)對(duì)象)

3.5 DivergentChange(發(fā)散式變化)79

  • 一個(gè)類受多種變化影響:加上一個(gè)功能需要修改類中多個(gè)函數(shù)
  • 目標(biāo)是每個(gè)對(duì)象都可以只因一種變化而需要修改
  • 方法:可以將提煉類來(lái)達(dá)到。

3.6 ShotgunSurgery(霰彈式修改)80

  • 遇到某種變化桐绒,需要在許多不同類做小修改。
  • 可以通過(guò)移動(dòng)函數(shù)之拨、移動(dòng)字段茉继、內(nèi)聯(lián)類把一種變化一系列變化放到同一個(gè)類中。
  • 對(duì)比:DivergentChange(發(fā)散式變化)是一個(gè)類受多個(gè)變化影響蚀乔;ShotgunSurgery(霰彈式修改)是一個(gè)變化引起多個(gè)類相應(yīng)修改烁竭。

3.7 FeatureEnvy(依戀情結(jié))80

  • 函數(shù)對(duì)某個(gè)類的興趣高過(guò)對(duì)自己類的興趣
  • 通過(guò)移動(dòng)函數(shù)放到該合適的位置。

3.8 DataClumps(數(shù)據(jù)泥團(tuán))81

  • 數(shù)據(jù)項(xiàng)總是成群結(jié)隊(duì)出現(xiàn)
  • 判斷方法:刪除眾多數(shù)據(jù)項(xiàng)的一項(xiàng)吉挣,這么做其他數(shù)據(jù)是否失去意義派撕。如果不再有意義就需要提煉為參數(shù)對(duì)象。

3.9 PrimitiveObsession(基本類型偏執(zhí))81

  • 有些字段可以用對(duì)象表示更準(zhǔn)確ReplaceDataValuewithObject(以對(duì)象取代數(shù)據(jù)值)
  • 對(duì)于不影響行為的類型碼可以ReplaceTypeCodewithClass(以類取代類型碼)
  • 影響行為的類型碼可以ReplaceTypeCodewithSubclasses(以子類取代類型碼)睬魂,類型碼在運(yùn)行時(shí)會(huì)變化就用ReplaceTypeCodewithState/Strategy(以State/Strategy取代類型碼)

3.10 SwitchStatements(switch驚悚現(xiàn)身)82

  • 使用ReplaceTypeCodewithSubclasses(以子類取代類型碼)或者ReplaceTypeCodewithState/Strategy(以State/Strategy取代類型碼)
  • 輕量級(jí)的解決方法:ReplaceParameterwithExplicitMethods(以明確函數(shù)取代參數(shù))

3.11 ParallelInheritanceHierarchies(平行繼承體系)83

  • 每當(dāng)為一個(gè)類增加子類必須也為另外一個(gè)類增加一個(gè)子類
  • 策略是讓一個(gè)繼承體系的實(shí)例引用另一個(gè)繼承體系的實(shí)例终吼。

3.12 LazyClass(冗贅類)83

  • 內(nèi)聯(lián)類或者Collapse Hierarchy(折疊繼承體系)來(lái)解決

3.13 SpeculativeGenerality(夸夸其談未來(lái)性)83

  • 內(nèi)聯(lián)類或者Collapse Hierarchy(折疊繼承體系)來(lái)解決
  • 函數(shù)參數(shù)沒(méi)被用上RemoveParameter(移除參數(shù))
  • 函數(shù)名稱過(guò)于抽象RenameMethod(函數(shù)改名)

3.14 TemporaryField(令人迷惑的暫時(shí)字段)84

  • 對(duì)象中某個(gè)字段僅為特定情況而設(shè)。
  • 提煉類來(lái)解決

3.15 MessageChains(過(guò)度耦合的消息鏈)84

  • 獲取一個(gè)對(duì)象氯哮,再通過(guò)該對(duì)象獲取另外一個(gè)對(duì)象進(jìn)行操作:HideDelegate(隱藏“委托關(guān)系”)

3.16 MiddleMan(中間人)85

  • 過(guò)度委托形成中間人:RemoveMiddleMan(移除中間人)
  • 如果中間人還有其他行為,Replace Delegation with Inherited(以繼承取代委托)

3.17 InappropriateIntimacy(狎昵關(guān)系)85

  • 兩個(gè)類過(guò)于親密际跪,花費(fèi)太多時(shí)間去探究彼此private成分
  • 移動(dòng)字段和移動(dòng)方法減少狎昵
  • ChangeBidirectionalAssociationtoUnidirectional(將雙向關(guān)聯(lián)改為單向關(guān)聯(lián))
  • 如果兩個(gè)類實(shí)在情投意合:可以使用ExtractClass(提煉類),讓他們使用新類進(jìn)行交互。

3.18 AlternativeClasseswithDifferentInterfaces(異曲同工的類)85

  • 兩個(gè)函數(shù)做了相同的事情卻有不同的簽名

3.19 IncompleteLibraryClass(不完美的庫(kù)類)86

  • 庫(kù)函數(shù)不夠好,需要加入一些操作姆打,其實(shí)類似于 適配IntroduceForeignMethod(引入外加函數(shù))
  • 如果需要加入大量的操作良姆,IntroduceLocalExtension(引入本地?cái)U(kuò)展)

3.20 DataClass(純稚的數(shù)據(jù)類)86

  • 類只有數(shù)據(jù)沒(méi)有行為,其他類存在對(duì)該類的數(shù)據(jù)進(jìn)行取值設(shè)值操作
  • 有public字段:EncapsulateField(封裝字段)
  • 對(duì)于不該被其他類修改的字段:RemoveSettingMethod(移除設(shè)值函數(shù))

3.21 RefusedBequest(被拒絕的遺贈(zèng))87

  • 如果類不想得到另一個(gè)類全部東西幔戏,只對(duì)部分感興趣玛追。
  • 可以使用Replace inherited with Delegation(以委托取代繼承)來(lái)處理

3.22 Comments(過(guò)多的注釋)87

  • 試試提煉方法來(lái)解決注釋過(guò)多問(wèn)題

第4章 構(gòu)筑測(cè)試體系89

4.1 自測(cè)試代碼的價(jià)值89

4.2 JUnit測(cè)試框架91

4.3 添加更多測(cè)試97

第5章 重構(gòu)列表103

5.1 重構(gòu)的記錄格式103

5.2 尋找引用點(diǎn)105

5.3 這些重構(gòu)手法有多成熟106

第6章 重新組織函數(shù)109

6.1 ExtractMethod(提煉函數(shù))110

  • 無(wú)局部變量:直接抽取方法
  • 含有局部變量
    • 局部變量只在提煉代碼塊內(nèi)被讀取值:將局部變量作為方法參數(shù)
    • 局部變量在提煉代碼塊內(nèi)被賦值:1只在提煉代碼內(nèi)被使用->將局部變量提煉到新該方法內(nèi);2在提煉代碼塊后->使用就返回局部變量修改后的值

6.2 InlineMethod(內(nèi)聯(lián)函數(shù))117

  • 當(dāng)函數(shù)的名稱與其本體都一眼清晰明了,在函數(shù)調(diào)用點(diǎn)插入函數(shù)本體闲延,移除該函數(shù)痊剖。
  • 有一群不甚合理的函數(shù),可以先內(nèi)聯(lián)到大型函數(shù)然后再提煉出合理的小函數(shù)

6.3 InlineTemp(內(nèi)聯(lián)臨時(shí)變量)119

  • 當(dāng)臨時(shí)變量只是被一個(gè)簡(jiǎn)單表達(dá)式賦值一次慨代,而它妨礙其他重構(gòu)方法
  • 方法:將所有對(duì)該變量的引用動(dòng)作替代成對(duì)它賦值的表達(dá)式本身邢笙。
  • 情形:
    • InlineTemp多半是為ReplaceTempwithQuery(以查詢?nèi)〈R時(shí)變量)準(zhǔn)備
    • 臨時(shí)變量被一次賦值后,臨時(shí)變量作為函數(shù)的返回值侍匙。

6.4 ReplaceTempwithQuery(以查詢?nèi)〈R時(shí)變量)120

  • 情況:你的程序以一個(gè)臨時(shí)變量保存一個(gè)表達(dá)式的計(jì)算結(jié)果
  • 做法:將表達(dá)式提煉出獨(dú)立的函數(shù)氮惯,然后臨時(shí)變量的調(diào)用替換成新函數(shù)的調(diào)用。此后新函數(shù)也能被調(diào)用想暗。
  • 具體做法:
    • 將提煉出來(lái)的函數(shù)用private修飾
    • 如果獨(dú)立函數(shù)有副作用妇汗,那對(duì)它進(jìn)行SeparateQueryfromModifier(將查詢函數(shù)和修改函數(shù)分離)

6.5 IntroduceExplainingVariable(引入解釋性變量)124

  • 將復(fù)雜表達(dá)式的結(jié)果賦值給一個(gè)臨時(shí)變量,用臨時(shí)變量名稱來(lái)解釋表達(dá)式的用途

6.6 SplitTemporaryVariable(分解臨時(shí)變量)128

  • 臨時(shí)變量被賦值超過(guò)一次说莫,但是既不是 循環(huán)變量也不是被用于 收集計(jì)算結(jié)果
  • 原因:一個(gè)變量應(yīng)該承擔(dān)一個(gè)責(zé)任杨箭,如果被賦值多次很可能承擔(dān)了多個(gè)責(zé)任
  • 做法:針對(duì)每次賦值,創(chuàng)建新的臨時(shí)變量

6.7 RemoveAssignmentstoParameters(移除對(duì)參數(shù)的賦值)131

  • java是值傳遞储狭,對(duì)參數(shù)的任何修改都不會(huì)再調(diào)用端造成影響互婿,所以對(duì)于 用過(guò)引用傳遞的人可能會(huì)發(fā)生理解錯(cuò)誤
  • 參數(shù)應(yīng)該僅表示“被傳遞過(guò)來(lái)的東西”

6.8 ReplaceMethodwithMethodObject(以函數(shù)對(duì)象取代函數(shù))135

  • 情形:在大型函數(shù)內(nèi),對(duì)局部變量的使用導(dǎo)致難以使用ExtractMethod(提煉函數(shù))進(jìn)行重構(gòu)
  • 做法:將這個(gè)函數(shù)放入一個(gè)對(duì)象里辽狈,局部變量變成對(duì)象成員變量慈参,然后可以在同一對(duì)象中將這個(gè)大型函數(shù)分解為多個(gè)小型函數(shù)。
  • 原因:局部變量會(huì)增加分解函數(shù)的困難度

6.9 SubstituteAlgorithm(替換算法)139

  • 把某個(gè)算法替換成更清晰的做法(算法)(有點(diǎn)廢話)刮萌。

第7章 在對(duì)象之間搬移特性141

7.1 MoveMethod(搬移函數(shù))142

  • 情形:程序中有個(gè)函數(shù)與所駐類之外的另一個(gè)類進(jìn)行更多交流驮配,調(diào)用后者或者后者調(diào)用該函數(shù)
  • 做法:在該函數(shù)最常引用的類中定義相似行為的新接口,將舊函數(shù)變成委托函數(shù)或者將舊函數(shù)刪除着茸。
  • 具體做法:
    • 檢查源類中被源函數(shù)使用的一切特性壮锻,如果特性被其他函數(shù)使用,考慮這些函數(shù)一起搬移
    • 檢查源類的子類和超類涮阔,看看是否有該函數(shù)的聲明猜绣,如果出現(xiàn),很可能不能搬移敬特。
    • 目標(biāo)類需要使用源類的特性:1將該特性轉(zhuǎn)移到目標(biāo)類途事;2建立目標(biāo)類到源類之間引用验懊。3將源類作為參數(shù)傳給目標(biāo)類4將該特性作為參數(shù)傳給目標(biāo)類
    • 如果源函數(shù)包含 異常處理,需要考慮是在目標(biāo)類還是源函數(shù)處理

7.2 MoveField(搬移字段)146

  • 情形:程序中有個(gè)字段與所駐類之外被另一個(gè)類使用(包括設(shè)置取值函數(shù)的間接調(diào)用)尸变,后者調(diào)用該字段
  • 做法:將該字段搬移到目標(biāo)類
  • 具體做法:建立從“舊類訪問(wèn)新類”的連接關(guān)系义图,除非真正需要 不要建立從“新類到舊類”的關(guān)系

7.3 ExtractClass(提煉類)149

  • 情形:一個(gè)類做了兩個(gè)類的事
  • 做法:建立新類,將相應(yīng)的字段和函數(shù)放到新類

7.4 InlineClass(將類內(nèi)聯(lián)化)154

  • 情形:某個(gè)類沒(méi)做太多的事情召烂,與ExtractClass(提煉類)相反
  • 做法:將這個(gè)類的所有特性搬移到另一類中碱工,移除該類。
  • 判斷依據(jù):當(dāng)一個(gè)類不再承擔(dān)足夠責(zé)任

7.5 HideDelegate(隱藏“委托關(guān)系”)157

  • 情形:客戶端通過(guò)委托類來(lái)調(diào)用另一個(gè)對(duì)象
  • 做法:在服務(wù)類上建立客戶端所需的函數(shù)奏夫,然后隱藏委托關(guān)系
  • 依據(jù):符合“封裝”的特性怕篷。當(dāng)委托類發(fā)生變化不會(huì)對(duì)客戶端造成影響

7.6 RemoveMiddleMan(移除中間人)160

  • 情形:某個(gè)類做了過(guò)多的委托動(dòng)作
  • 做法:讓客戶端直接調(diào)用委托類
  • 依據(jù):當(dāng)原委托類的特性越來(lái)越多,服務(wù)類的委托函數(shù)將越來(lái)越長(zhǎng)酗昼,需要讓客戶端直接調(diào)用廊谓,避免服務(wù)類淪為中間人。

7.7 IntroduceForeignMethod(引入外加函數(shù))162

  • 情形:需要為服務(wù)類某個(gè)函數(shù)增加功能麻削,但是不能修改該類
  • 做法:新建函數(shù)并將服務(wù)類的對(duì)象實(shí)例作為參數(shù)傳入蒸痹。
  • 具體情形:如果需要為服務(wù)類增加 大量的方法,請(qǐng)考慮使用IntroduceLocalExtension(引入本地?cái)U(kuò)展)

7.8 IntroduceLocalExtension(引入本地?cái)U(kuò)展)164

  • 情形:需要為服務(wù)類某個(gè)函數(shù)增加函數(shù)呛哟,但是不能修改該類
  • 做法:建立新類叠荠,使它包括這些額外函數(shù),讓這個(gè)擴(kuò)展類作為服務(wù)類的子類或者包裝類扫责。
  • 具體情況:如果需要對(duì)數(shù)據(jù)進(jìn)行修改要波及服務(wù)類對(duì)象榛鼎,那么使用包裝類的方式。如果不需要鳖孤,使用子類化的方式

第8章 重新組織數(shù)據(jù)169

8.1 SelfEncapsulateField(自封裝字段)171

  • 情形:直接訪問(wèn)一個(gè)字段者娱,但是字段之間的耦合關(guān)系逐漸變得笨拙。
  • 做法:自封裝就是在對(duì)于類內(nèi)部的字段也封裝一個(gè)設(shè)值取值的函數(shù)苏揣。
  • 爭(zhēng)論:字段訪問(wèn)方式是直接訪問(wèn)還是間接訪問(wèn)一致?tīng)?zhēng)論不斷
  • 間接訪問(wèn)的好處
    • 修改獲取數(shù)據(jù)的途徑肺然;
    • 支持更靈活的數(shù)據(jù)管理;如延遲加載(需要用到才加載)等腿准。
  • 直接訪問(wèn)的好處
    • 容易閱讀代碼,不會(huì)需要轉(zhuǎn)換一下這個(gè)函數(shù)是取值函數(shù)拾碌。

8.2 ReplaceDataValuewithObject(以對(duì)象取代數(shù)據(jù)值)175

  • 情形:假如一個(gè)數(shù)據(jù)項(xiàng)需要與其他數(shù)據(jù)一起使用才有意義吐葱。
  • 做法:將數(shù)據(jù)變成對(duì)象。

8.3 ChangeValuetoReference(將值對(duì)象改為引用對(duì)象)179

  • 情形:從一個(gè)類衍生出彼此相似的對(duì)象的實(shí)例校翔,希望把它們替換為同一個(gè)對(duì)象弟跑,ps:方便統(tǒng)一修改
  • 做法:將值對(duì)象變成引用對(duì)象
  • 區(qū)別:
    • 引用對(duì)象每個(gè)都對(duì)應(yīng)現(xiàn)實(shí)中一個(gè)對(duì)象(==)
    • 值對(duì)象只關(guān)心其值是否相等。(重寫equals()和hashcode()方法)
  • 具體做法:
    • 需要使用工廠模式來(lái)創(chuàng)建對(duì)象
    • 需要一個(gè)類(或者是自身)用字典或者靜態(tài)表來(lái)保存對(duì)象
    • 決定對(duì)象是預(yù)先創(chuàng)建還是動(dòng)態(tài)創(chuàng)建

8.4 ChangeReferencetoValue(將引用對(duì)象改為值對(duì)象)183

  • 情形:有一個(gè)引用對(duì)象且 很小(創(chuàng)建太多值對(duì)象內(nèi)存消耗大) 不可變(無(wú)需修改對(duì)象)防症,那么應(yīng)該將其轉(zhuǎn)換為值對(duì)象
  • 具體做法:
    • 查看是否是不可變對(duì)象或者可修改成不可變對(duì)象
    • 重寫hashCode和equals()方法
    • 取消使用工廠模式和將對(duì)象的構(gòu)造函數(shù)設(shè)為public

8.5 ReplaceArraywithObject(以對(duì)象取代數(shù)組)186

  • 情形:如果數(shù)據(jù)存儲(chǔ)了不相似的數(shù)據(jù)孟辑,元素代表不同的東西哎甲。
  • 做法:將數(shù)組變成對(duì)象,數(shù)組的每個(gè)元素用字段表示

8.6 DuplicateObservedData(復(fù)制“被監(jiān)視數(shù)據(jù)”)189

  • 情形: 有領(lǐng)域數(shù)據(jù)置身于GUI控件中饲嗽,而領(lǐng)域函數(shù)需要訪問(wèn)這些數(shù)據(jù)
  • 做法:將該數(shù)據(jù)復(fù)制到領(lǐng)域模型中炭玫。建立Observer模式,同步UI和領(lǐng)域模型的數(shù)據(jù)貌虾。

8.7 ChangeUnidirectionalAssociationtoBidirectional(將單向關(guān)聯(lián)改為雙向關(guān)聯(lián))197

  • 情形:被引用類需要得到引用類做一些處理
  • 具體做法:
    • 兩者是一對(duì)多關(guān)系吞加,有單一引用承擔(dān)控制關(guān)聯(lián)關(guān)系責(zé)任
    • 如果某個(gè)對(duì)象(Task)是另一個(gè)對(duì)象(Project)的組件,由后者負(fù)責(zé)控制尽狠。
    • 如果兩者之間都是多對(duì)多關(guān)系衔憨,那么由誰(shuí)負(fù)責(zé)都沒(méi)關(guān)系

8.8 ChangeBidirectionalAssociationtoUnidirectional(將雙向關(guān)聯(lián)改為單向關(guān)聯(lián))200

  • 情形:兩個(gè)類有雙向關(guān)聯(lián),但是一個(gè)類不關(guān)心另一個(gè)類的特性
  • 做法:去除雙向關(guān)聯(lián)
  • 原因:
    • 雙向關(guān)聯(lián)可能造成僵尸對(duì)象袄膏,不能被清除釋放內(nèi)存践图。
    • 使兩個(gè)類存在耦合關(guān)系,一個(gè)類的變化會(huì)導(dǎo)致另一類的變化沉馆。

8.9 ReplaceMagicNumberwithSymbolicConstant(以字面常量取代魔法數(shù))204

  • 情形:有一個(gè)字面常量(除了0和1之外)
  • 做法:創(chuàng)建常量賦值以該字面常量码党,給予命名。

8.10 EncapsulateField(封裝字段)206

  • 情形:一個(gè)類有public字段
  • 將它聲明為private悍及,并提供相應(yīng)的訪問(wèn)函數(shù)

8.11 EncapsulateCollection(封裝集合)208

  • 情形:有函數(shù)返回集合
  • 做法:讓該函數(shù)返回只讀副本闽瓢,并在該類提供增加和刪除集合元素的函數(shù)
  • 具體做法:不應(yīng)該提供集合的設(shè)值函數(shù)

8.12 ReplaceRecordwithDataClass(以數(shù)據(jù)類取代記錄)217

  • 情形:面對(duì)傳統(tǒng)編程環(huán)境的記錄數(shù)據(jù)
  • 做法:為該記錄創(chuàng)建一個(gè)“啞”數(shù)據(jù)對(duì)象。

8.13 ReplaceTypeCodewithClass(以類取代類型碼)218

  • 情形:類中有個(gè)數(shù)值型類型碼心赶,不影響類的行為
  • 做法:以一個(gè)新類替代類型碼

8.14 ReplaceTypeCodewithSubclasses(以子類取代類型碼)223

  • 情形:有一個(gè)不可變的類型碼且影響類的行為
  • 做法:以子類取代這個(gè)類型碼

8.15 ReplaceTypeCodewithState/Strategy(以State/Strategy取代類型碼)227

  • 情形:有一個(gè)類型碼且影響類的行為扣讼,但是無(wú)法通過(guò)繼承消除(類型碼可變化)
  • 做法:以狀態(tài)對(duì)象取代。

8.16 ReplaceSubclasswithFields(以字段取代子類)232

  • 情形:各個(gè)子類唯一區(qū)別只在“返回常量的數(shù)據(jù)”的函數(shù)上
  • 做法:修改這些函數(shù)使它們返回超類的某個(gè)(新增)字段缨叫,然后銷毀子類椭符。

第9章 簡(jiǎn)化條件表達(dá)式237

9.1 DecomposeConditional(分解條件表達(dá)式)238

  • 情形:if-then-else語(yǔ)句,不同分支做不同事情形成大型函數(shù)耻姥,本身就難以閱讀销钝,尤其在帶有復(fù)雜條件的邏輯中。
  • 做法:
    • 將if語(yǔ)句提煉為函數(shù)
    • 將then和else段落提煉為函數(shù)
    • 存在 嵌套琐簇,先判斷是否可以用ReplaceNestedConditionalwithGuardClauses(以衛(wèi)語(yǔ)句取代嵌套條件表達(dá)式)消除蒸健。不行再分解每個(gè)條件

9.2 ConsolidateConditionalExpression(合并條件表達(dá)式)240

  • 情形:有一系列條件判斷都返回相同結(jié)果
  • 做法:將這些測(cè)試合并為同一個(gè)表達(dá)式并將這個(gè)表達(dá)式提煉為獨(dú)立函數(shù)
  • 原因:
    • 只是一次檢查,只是存在并列條件需要檢查而已
    • 為ExtractMethod(提煉函數(shù))做準(zhǔn)備婉商,通過(guò) 函數(shù)名 告知“為什么這么做”
  • 特殊情形:條件表達(dá)式 存在副作用似忧。

9.3 ConsolidateDuplicateConditionalFragments(合并重復(fù)的條件片段)243

  • 情形:在條件表達(dá)式的分支上有相同的代碼
  • 做法:將這段重復(fù)代碼搬移到條件表達(dá)式之外,多行代碼可以提煉為獨(dú)立函數(shù)丈秩。
  • 當(dāng)try和catch執(zhí)行相同代碼盯捌,可以將代碼移到final區(qū)段。

9.4 RemoveControlFlag(移除控制標(biāo)記)245

  • 情形:在一系列布爾表達(dá)式中蘑秽,某個(gè)變量存在控制標(biāo)記(control flag)作用饺著。
  • 做法:以break或者continue代替

9.5 ReplaceNestedConditionalwithGuardClauses(以衛(wèi)語(yǔ)句取代嵌套條件表達(dá)式)250

  • 情形:函數(shù)中的條件邏輯使人難以看清正確的執(zhí)行路徑箫攀。
  • 做法:使用衛(wèi)語(yǔ)句表現(xiàn)特殊情況

9.6 ReplaceConditionalwithPolymorphism(以多態(tài)取代條件表達(dá)式)255

  • 情形:存在條件表達(dá)式根據(jù)對(duì)象的類型不同選擇不同的行為
  • 做法:將表達(dá)式分支放進(jìn)不同子類的重寫方法,將原始函數(shù)提煉為抽象函數(shù)幼衰。

9.7 IntroduceNullObject(引入Null對(duì)象)260

  • 情形:需要再三檢查對(duì)象是否為null
  • 做法:將null值替代為null對(duì)象

9.8 IntroduceAssertion(引入斷言)267

  • 情形:某段代碼需要對(duì)程序狀態(tài)做出某種假設(shè)
  • 做法:以斷言明確表現(xiàn)這種假設(shè)
  • 具體做法:
    • 斷言在 發(fā)布的時(shí)候統(tǒng)統(tǒng) 被去除
    • 斷言應(yīng)該檢查 ***一定必須為真 *** 的條件

第10章 簡(jiǎn)化函數(shù)調(diào)用271

10.1 RenameMethod(函數(shù)改名)273

  • 情形:命名不好
  • 做法:
    • 增加函數(shù)靴跛,將舊函數(shù)代碼復(fù)制到新函數(shù)
    • 修改舊函數(shù),讓其轉(zhuǎn)發(fā)調(diào)用新函數(shù)塑顺,如果舊函數(shù)引用點(diǎn)少可跳過(guò)
    • 編譯測(cè)試
    • 找出舊函數(shù)引用汤求,調(diào)用新函數(shù)
    • 編譯測(cè)試
    • 刪除舊函數(shù)

10.2 AddParameter(添加參數(shù))275

  • 情形:某個(gè)函數(shù)需要調(diào)用端更多的信息
  • 做法:為此函數(shù)添加對(duì)象參數(shù),讓該對(duì)象帶進(jìn)函數(shù)所需信息严拒。
  • 其他考慮:
    • 現(xiàn)有參數(shù)是否提供足夠的信息扬绪?
    • 這個(gè)函數(shù)是否應(yīng)該移動(dòng)到擁有該信息的對(duì)象中?
    • 加入新參數(shù)是否合適裤唠,是否需要使用IntroduceParameterObject(引入?yún)?shù)對(duì)象)

10.3 RemoveParameter(移除參數(shù))277

  • 情形:函數(shù)不需要某個(gè)參數(shù)
  • 做法:將該參數(shù)移除
  • 具體做法同10.1

10.4 SeparateQueryfromModifier(將查詢函數(shù)和修改函數(shù)分離)279

  • 情形:某個(gè)函數(shù)既返回對(duì)象狀態(tài)值挤牛,又修改對(duì)象狀態(tài)
  • 做法:建立兩個(gè)不同的函數(shù),其中一個(gè)負(fù)責(zé)查詢种蘸,另一個(gè)負(fù)責(zé)修改墓赴。
  • 原則:任何一個(gè)有返回值的函數(shù)都不應(yīng)該有副作用。
  • 多線程:將修改和查詢函數(shù)封裝在一個(gè)同步函數(shù)中分開(kāi)調(diào)用航瞭。

10.5 ParameterizeMethod(令函數(shù)攜帶參數(shù))283

  • 情形:若干個(gè)函數(shù)做了類似的工作诫硕,但在函數(shù)本體中卻包含了不同的值。
  • 做法:建立單一函數(shù)刊侯,以參數(shù)表達(dá)那些不同的值章办。
  • 要點(diǎn): 可將少量數(shù)值視為參數(shù)

10.6 ReplaceParameterwithExplicitMethods(以明確函數(shù)取代參數(shù))285

  • 情形:有個(gè)函數(shù)完全有數(shù)值不同采取不同的行為
  • 做法:針對(duì)該參數(shù)的每個(gè)可能值,建立獨(dú)立函數(shù)滨彻。
  • 對(duì)比:與ParameterizeMethod(令函數(shù)攜帶參數(shù))相反
  • 目的:提供清晰的入口藕届。
  • 如果參數(shù)值對(duì)函數(shù)行為影響不大,不應(yīng)該采用此方法亭饵。

10.7 PreserveWholeObject(保持對(duì)象完整)288

  • 情形:從某個(gè)對(duì)象取若干值休偶,把他們作為參數(shù)傳給函數(shù)
  • 做法:改為調(diào)用整個(gè)對(duì)象
  • 目的:避免過(guò)長(zhǎng)參數(shù)
  • 不使用該方法:
    • 如果函數(shù)只依賴那些值不依賴對(duì)象,那么不能采用此方法辜羊,會(huì)導(dǎo)致耦合
    • 有時(shí)候函數(shù)使用了很多來(lái)自對(duì)象的數(shù)據(jù)踏兜,那么應(yīng)該考慮使用(Move Method)

10.8 ReplaceParameterwithMethods(以函數(shù)取代參數(shù))292

  • 情形:對(duì)象調(diào)用某個(gè)函數(shù),并將所得結(jié)果作為參數(shù)傳遞給另一個(gè)函數(shù)八秃,而接受該參數(shù)的函數(shù)本身也能夠調(diào)用前一個(gè)函數(shù)
  • 做法:讓參數(shù)接受者去除該項(xiàng)參數(shù)碱妆,并直接調(diào)用前一個(gè)函數(shù)

10.9 IntroduceParameterObject(引入?yún)?shù)對(duì)象)295

  • 情形:有些參數(shù)總是自然地同時(shí)出現(xiàn)
  • 做法:以一個(gè)對(duì)象取代這些參數(shù)
  • 目的:縮短參數(shù)長(zhǎng)度,函數(shù)具有一致性喜德,降低理解和修改代碼的難度

10.10 RemoveSettingMethod(移除設(shè)值函數(shù))300

  • 情形:類的某個(gè)字段應(yīng)該對(duì)象創(chuàng)建的時(shí)候被設(shè)置,然后不再改變
  • 做法:去掉該字段的設(shè)置函數(shù)

10.11 HideMethod(隱藏函數(shù))303

  • 情形:有一個(gè)函數(shù)垮媒,從來(lái)沒(méi)有被任何類調(diào)用
  • 做法:將該函數(shù)設(shè)為private

10.12 ReplaceConstructorwithFactoryMethod(以工廠函數(shù)取代構(gòu)造函數(shù))304

  • 情形:創(chuàng)建對(duì)象時(shí)不僅僅是做簡(jiǎn)單的構(gòu)建動(dòng)作
  • 做法:將構(gòu)造函數(shù)替換為工廠模式

10.13 EncapsulateDowncast(封裝向下轉(zhuǎn)型)308

  • 情形:某個(gè)函數(shù)返回的對(duì)象舍悯,需要由函數(shù)調(diào)用者執(zhí)行向下轉(zhuǎn)型()downcast
  • 做法:將向下轉(zhuǎn)型移到函數(shù)中

10.14 ReplaceErrorCodewithException(以異常取代錯(cuò)誤碼)310

  • 情形:某個(gè)函數(shù)返回一個(gè)特定的代碼航棱,表示某個(gè)錯(cuò)誤的情況
  • 做法:改用異常

10.15 ReplaceExceptionwithTest(以測(cè)試取代異常)315

  • 情形:面對(duì)一個(gè)調(diào)用者可以預(yù)先檢查條件,你拋出了一個(gè)異常
  • 做法:修改調(diào)用者萌衬,使它在調(diào)用函數(shù)之前檢查饮醇。

第11章 處理概括關(guān)系319

11.1 PullUpField(字段上移)320

  • 情形:兩個(gè)子類擁有相同的字段
  • 做法:將該字段移動(dòng)到超類,去除重復(fù)數(shù)據(jù)聲明和關(guān)于數(shù)據(jù)的重復(fù)行為秕豫。并堆超類該字段使用-SelfEncapsulateField(自封裝字段)

11.2 PullUpMethod(函數(shù)上移)322

  • 情形:有些函數(shù)朴艰,在各個(gè)子類產(chǎn)生相同的結(jié)果。
  • 做法:將該函數(shù)移動(dòng)到超類

11.3 PullUpConstructorBody(構(gòu)造函數(shù)本體上移)325

  • 情形:你在各個(gè)子類擁有一些構(gòu)造函數(shù)混移,它們的本地幾乎完全一致
  • 做法:在超類新建一個(gè)構(gòu)造函數(shù)祠墅,并在子類構(gòu)造函數(shù)中調(diào)用它。
  • 具體做法:
    • 將共同代碼復(fù)制到超類構(gòu)造函數(shù)中
    • 將共同代碼放在子類構(gòu)造函數(shù)起始處歌径,然后再?gòu)?fù)制到超類構(gòu)造函數(shù)中毁嗦。
    • 將子類構(gòu)造函數(shù)中共同代碼刪除,改用調(diào)用新建的超類構(gòu)造函數(shù)回铛。

11.4 PushDownMethod(函數(shù)下移)328

  • 情形:超類中的某個(gè)函數(shù)只與部分而非全部子類有關(guān)
  • 做法:將這個(gè)函數(shù)移到相關(guān)的子類去狗准。

11.5 PushDownField(字段下移)329

  • 情形:超類中的某個(gè)字段只被部分而非全部子類使用
  • 做法:將這個(gè)字段移到需要它的那些子類去。

11.6 ExtractSubclass(提煉子類)330

  • 情形:類中的某些特性只被某些而非全部實(shí)例用到茵肃。
  • 做法:新建一個(gè)子類腔长,將上面所說(shuō)的那一部分特性移到子類中。
  • 具體情況:
    • 并不是出現(xiàn)類型碼就表示需要用到子類验残,可以在委托和繼承之間做選擇捞附。
    • 為子類新建構(gòu)造函數(shù),如果需要 隱藏子類胚膊,可使用ReplaceConstructorwithFactoryMethod(以工廠函數(shù)取代構(gòu)造函數(shù))
      • 找出超類調(diào)用點(diǎn)故俐,如超類構(gòu)造函數(shù)與子類不同,通過(guò)rename method方法可以解決紊婉。
      • 如果不需要超類實(shí)例药版,可以將超類聲明為抽象類。
    • 逐一使用字段下移喻犁、函數(shù)下移將源類的特性移動(dòng)到子類槽片。

11.7 ExtractSuperclass(提煉超類)336

  • 情形:兩個(gè)類有相似特性。
  • 做法:為兩個(gè)類建立一個(gè)超類肢础,將相同特性移至超類还栓。

11.8 Extract Interface(提煉接口)341

  • 情形:某組用戶只使用類責(zé)任區(qū)中一個(gè)特定子集或者兩個(gè)類的接口有部分相同。
  • 做法:將相同子集提煉到獨(dú)立的接口中传轰。
  • 區(qū)別:提煉超類是提煉共同代碼剩盒,提煉接口時(shí)提煉共同接口。
  • 具體情形:如果某個(gè)類在不同環(huán)境下扮演截然不同的角色慨蛙,使用接口就是個(gè)好主意辽聊。

11.9 Collapse Hierarchy(折疊繼承體系)344

  • 情形:超類和子類之間區(qū)別不大纪挎。
  • 做法:將它們合為一體。

11.10 Form TemPlate Method(塑造模板函數(shù))344

  • 情形:你有一些子類跟匆,其中相應(yīng)的函數(shù)以相同順序執(zhí)行類似的操作异袄,但各個(gè)操作的細(xì)節(jié)有所不同。
  • 做法:將這些小操作分別放進(jìn)獨(dú)立函數(shù)中玛臂,并保持它們都有相同的簽名烤蜕,于是原函數(shù)也變得相同了。然后將原函數(shù)上移至超類迹冤,運(yùn)用多態(tài)來(lái)避免重復(fù)代碼澡绩。
  • 原因:雖然使用了繼承娘香,但是函數(shù)重復(fù)應(yīng)盡量避免。

11.11 Replace inherited with Delegation(以委托取代繼承)352

  • 情形:某個(gè)子類只使用超類接口中一部分,或是根本不需要繼承而來(lái)的數(shù)據(jù)
  • 做法:在子類中新建一個(gè)字段用以保存超類诫舅,調(diào)整子類函數(shù)兢仰,令它委托超類匠璧,然后去掉兩者之間的繼承關(guān)系芽狗。

11.12 Replace Delegation with Inherited(以繼承取代委托)352

  • 情形:在兩個(gè)類之間使用委托關(guān)系,并經(jīng)常為整個(gè)接口編寫許多極簡(jiǎn)單的委托函數(shù)庶橱,
  • 做法:讓委托類繼承受托類贮勃。
  • 告誡:
    • 如果并沒(méi)有使用受托類的所有函數(shù),那么就不要使用這個(gè)方法苏章。因?yàn)樽宇悜?yīng)該總是遵循超類的接口寂嘉,如果委托過(guò)多可以通過(guò)移除“中間人”方法讓客戶端調(diào)用受托函數(shù),或者“提煉超類”枫绅,讓兩個(gè)類的接口提煉到超類中泉孩。類似的還可以使用“提煉接口”方法。
    • 如果受托對(duì)象被不止一個(gè)其他對(duì)象共享并淋,而且受托對(duì)象是可變的時(shí)候寓搬,那么這濕乎乎不能講委托關(guān)系替換為繼承關(guān)系。

……

第12章 大型重構(gòu)359

第13章 重構(gòu)县耽,復(fù)用與現(xiàn)實(shí)379

第14章 重構(gòu)工具401

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末句喷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子兔毙,更是在濱河造成了極大的恐慌唾琼,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澎剥,死亡現(xiàn)場(chǎng)離奇詭異锡溯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門祭饭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涌乳,“玉大人,你說(shuō)我怎么就攤上這事甜癞。” “怎么了宛乃?”我有些...
    開(kāi)封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵悠咱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我征炼,道長(zhǎng)析既,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任谆奥,我火速辦了婚禮眼坏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘酸些。我一直安慰自己宰译,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布魄懂。 她就那樣靜靜地躺著沿侈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪市栗。 梳的紋絲不亂的頭發(fā)上缀拭,一...
    開(kāi)封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音填帽,去河邊找鬼蛛淋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛篡腌,可吹牛的內(nèi)容都是我干的褐荷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哀蘑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诚卸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起绘迁,我...
    開(kāi)封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤合溺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缀台,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體棠赛,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了睛约。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鼎俘。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辩涝,靈堂內(nèi)的尸體忽然破棺而出贸伐,到底是詐尸還是另有隱情,我是刑警寧澤怔揩,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布捉邢,位于F島的核電站,受9級(jí)特大地震影響商膊,放射性物質(zhì)發(fā)生泄漏伏伐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一晕拆、第九天 我趴在偏房一處隱蔽的房頂上張望藐翎。 院中可真熱鬧,春花似錦实幕、人聲如沸吝镣。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赤惊。三九已至,卻和暖如春凰锡,著一層夾襖步出監(jiān)牢的瞬間未舟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工掂为, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裕膀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓勇哗,卻偏偏與公主長(zhǎng)得像昼扛,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欲诺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容