上一次的讀書分享活動(dòng)上面,已經(jīng)有幾位同事已經(jīng)分享了《重構(gòu)》這本書的讀后感,以及他們對(duì)重構(gòu)的一些認(rèn)識(shí)。從他們的分享上面,我已經(jīng)知道重構(gòu)的重要性和什么是重構(gòu)了。簡單一點(diǎn)說,重構(gòu)就是讓糟糕的代碼變成整潔的代碼。糟糕的代碼變成了整潔的代碼,好處很明顯,第一,自己看著舒服;第二,其他同事可以很快速的理解你的代碼,很容易維護(hù)你寫的代碼。
既然重構(gòu)這么好,為什么我們的系統(tǒng)很多代碼并沒有重構(gòu),舊的代碼很爛,新加的代碼還是很爛呢?
我認(rèn)為,第一,我們?nèi)狈?duì)整潔代碼的重視,包括公司層面,各個(gè)部門團(tuán)隊(duì),以及個(gè)人,我來公司的時(shí)候,公司以及團(tuán)隊(duì)并沒有跟我提這樣的要求,說要寫出整潔的代碼,面對(duì)需求,只要功能實(shí)現(xiàn),沒有bug ,不會(huì)對(duì)你的代碼質(zhì)量有過多的要求,所以,我們都是快速實(shí)現(xiàn)功能,并不會(huì)去太多思考怎么寫出整潔的代碼,所以我們寫出來的代碼是一種“流水型”,也叫面向過程的一種代碼,甚至很多時(shí)候,為了更快速的實(shí)現(xiàn),經(jīng)常會(huì)想到其他地方有類似功能實(shí)現(xiàn)的,就把代碼拷貝過來,改一改,然后測試一下,覺得ok,沒問題了,就交付出去了,以為沒有bug 就已經(jīng)做完了;第二,我們?nèi)狈χ貥?gòu)代碼的技能,覺得花時(shí)間去重構(gòu)對(duì)自己來說并不是一件容易的事情;第三,我想說的是,我們?nèi)狈σ环N追求,對(duì)整潔代碼的追求,對(duì)代碼藝術(shù)的追求,我們對(duì)代碼應(yīng)該要有一定程度的潔癖。
用簡單的語言總結(jié)一下,重構(gòu)就是讓代碼整潔,代碼不僅僅是寫給機(jī)器讀的,更重要的是要給人讀的,整潔的代碼其他人才能更容易,更輕松的讀懂,理解,維護(hù)才更容易。什么時(shí)候重構(gòu)呢?自己寫的代碼,寫完以后,交付之前,必須要重構(gòu);維護(hù)代碼,改代碼的時(shí)候,你應(yīng)該要重構(gòu);你去讀別人代碼,理解別人的代碼的時(shí)候,應(yīng)該重構(gòu);存在難以理解的代碼的要重構(gòu)。
帶著從書里面學(xué)習(xí)到一些重構(gòu)的技巧和方法的目的,我開始了本書的閱讀。
第一章,通過一個(gè)實(shí)例開啟了本書,用Java的多態(tài)進(jìn)行了代碼的重構(gòu)。通過識(shí)別代碼里面的壞味道,然后消除壞味道。
第二章,重構(gòu)的原則,用一句話總結(jié)一下,目的只有一個(gè),讓代碼整潔
第三章,代碼的壞味道,講了一些什么是代碼的壞味道,書里面說了很多壞味道的類型,簡單一點(diǎn)說吧,使代碼看起來不整潔,不容易理解的都是壞味道
第四章,講到了一個(gè)重構(gòu)的前提,要有單元測試,有了單元測試,第一,可以提前發(fā)現(xiàn)代碼中的問題;第二,進(jìn)行重構(gòu)的時(shí)候,心里可以更放心。最近,我也寫了一些單元測試,有的是代碼完成以后補(bǔ)上的,補(bǔ)單元測試的過程中,我對(duì)單元測試有這樣一個(gè)理解,單元測試,就好像是記錄自己讀代碼,理解代碼的一個(gè)過程,比如說,代碼中的一個(gè)if else邏輯塊,補(bǔ)單元測試的時(shí)候,我就會(huì)寫兩個(gè)case,第一個(gè),構(gòu)造一個(gè)滿足if條件的given ,第二個(gè),構(gòu)造一個(gè)滿足else的given,這個(gè)過程就好像是從代碼推導(dǎo)出需求的過程,跟TDD相反,這里也正體現(xiàn)了一個(gè)TDD的好處,從需求到Tasking,再到測試代碼,再到代碼實(shí)現(xiàn),這樣實(shí)現(xiàn)的代碼就緊跟需求了,這里就先不展開了。
第五章,重構(gòu)列表,感覺內(nèi)容有點(diǎn)過時(shí)。里面提到了一個(gè)重構(gòu)的基本技巧,小步前進(jìn),頻繁測試,小步其實(shí)比大步前進(jìn)速度會(huì)更快,因?yàn)樾〔讲蝗菀壮鲥e(cuò),每次的改動(dòng)都基于上一次沒有錯(cuò)誤的基礎(chǔ)上,步伐太大,出了問題之后,很可能要花更多的時(shí)間才能找出錯(cuò)誤,這個(gè)我在最近的TDD和OO Bootcamp培訓(xùn)里面都有體會(huì)到這個(gè)小步的過程。但自己在工作中,還是做得不夠好,沒有養(yǎng)成一個(gè)及時(shí)跑測試的習(xí)慣,
第六章,重新組織函數(shù)
1,提煉函數(shù),把過長的函數(shù)提煉出來,變成更小粒度的函數(shù)。這里我想分享一個(gè)我以前的一個(gè)相反的想法,很久以前,我去維護(hù)一個(gè)功能的時(shí)候,看代碼,然后從代碼的一個(gè)函數(shù)方法開始看,這個(gè)函數(shù)里面調(diào)用了很多其他的函數(shù)方法,函數(shù)方法里面有調(diào)用很多其他的函數(shù)方法,一直看下去,發(fā)現(xiàn)有很多層,我那個(gè)時(shí)候心里想,為什么不把這些代碼都放在一個(gè)函數(shù)方法里面,我一口氣就可以看完,那些代碼其實(shí)就比較好的用了提煉函數(shù)這個(gè)方法,為什么我當(dāng)時(shí)是那樣想的呢?第一,我那個(gè)時(shí)候自己就是寫一個(gè)很長的函數(shù)方法這樣寫代碼的;第二,我沒有采用快速理解代碼的方式去讀代碼,通過函數(shù)名理解函數(shù)的方式,而是采取每行代碼都要看的一種很笨的方式,所以,我要一層層的去找代碼,會(huì)覺得很累,也記不住那么多代碼邏輯;第三,我現(xiàn)在記不清那些具體的代碼是什么,有可能,那些代碼提煉出來的函數(shù)名字也取的不好,我看不出來他里面具體實(shí)現(xiàn)了什么,所以,提煉函數(shù)還有一個(gè)基本要做到的,就是要有很好的函數(shù)命名。

重構(gòu)之前的代碼,就好像我以前寫的代碼,而且是我以前比較贊同的一種寫法,要我以前寫,我覺得就應(yīng)該寫成這樣子。這里我要突出講的是里面的注釋,以前,我很贊同加注釋,我覺得,我在自己寫的代碼上面加注釋,是對(duì)其他人會(huì)有幫助的,對(duì)比一下重構(gòu)之后的函數(shù),我發(fā)現(xiàn),函數(shù)更加整潔,其實(shí)作者就是把重構(gòu)前的注釋信息當(dāng)成提煉出來函數(shù)的名字了,我以前那種想法的出發(fā)點(diǎn)其實(shí)是挺好的,只是方式不對(duì),這里重構(gòu)之后的函數(shù),其他人想讀懂函數(shù)就可以直接看主函數(shù),如果有必要知道更詳細(xì)的信息才需要看里面具體調(diào)用函數(shù)的實(shí)現(xiàn),這里我也想再次強(qiáng)調(diào)一點(diǎn),函數(shù)方法名很重要,
2. 內(nèi)聯(lián)函數(shù),和提煉函數(shù)可以說相反,去掉不必要的提煉
3.內(nèi)聯(lián)臨時(shí)變量
4.以查詢?nèi)〈R時(shí)變量,減少臨時(shí)變量,也有助于提煉函數(shù),用現(xiàn)在的工具,都比較容易做到
5. 引入解釋性變量,這個(gè)和以查詢?nèi)〈R時(shí)變量相反,為了代碼更好理解,把表達(dá)式用臨時(shí)變量解釋,但一般不這么做,更好的做法是用提煉函數(shù)的方法,把表達(dá)式分別放在不同的函數(shù)方法里面
6. 分解臨時(shí)變量,每個(gè)臨時(shí)變量不要承擔(dān)多個(gè)職責(zé)
7. 移除對(duì)參數(shù)的賦值,對(duì)參數(shù)賦值會(huì)很容易混淆,不容易理解代碼,可以用臨時(shí)變量來取代
以函數(shù)對(duì)象取代函數(shù),過多的參數(shù),導(dǎo)致很難提煉函數(shù),所以把提煉出來的函數(shù)變成一個(gè)對(duì)象,所有的局部變量都可以變成函數(shù)對(duì)象的字段。
8. 以函數(shù)對(duì)象取代函數(shù)
9. 替換算法,用更好更整潔的代碼替換舊的算法
第七章 在對(duì)象之間搬移特征
1,搬移函數(shù)
把函數(shù)放在更合適的地方,
2,搬移字段
跟搬移函數(shù)類似,把字段放在更合適的對(duì)象里面。
3,提煉類
每個(gè)類都是一個(gè)清楚的抽象,處理明確的責(zé)任,可以把大的類,分成小的類,每個(gè)類負(fù)責(zé)自己的事情。
4,將類內(nèi)聯(lián)化
和提煉類相反。
5,隱藏“委托關(guān)系”
利用Java的封裝特征,隱藏對(duì)象中的對(duì)象。
6,移出中間人
和隱藏委托關(guān)系相反。
7,引入外加函數(shù)
這個(gè)有點(diǎn)像提煉函數(shù)。
8,引入本地?cái)U(kuò)展
調(diào)用的類,不能滿足自己需求,又不能改動(dòng),可以創(chuàng)建一個(gè)新類繼承,然后再加自己的需要的功能。
總結(jié)一下吧,這一章節(jié),講了很多詳細(xì)的方法說明怎么去重構(gòu),可以注意到里面有很多方法是倆倆對(duì)應(yīng)完全相反的。所以,這些重構(gòu)的手法并不是生搬硬套的公式,重構(gòu)的時(shí)候應(yīng)該靈活運(yùn)用,需要在工作中慢慢積累經(jīng)驗(yàn)。
第八章,重新組織數(shù)據(jù)
1,自封裝字段
Java的特征之一,這個(gè)也沒有一定的標(biāo)準(zhǔn),可以結(jié)合實(shí)際情況運(yùn)用,這里提到一個(gè)比較明顯的好處,當(dāng)類擴(kuò)展的時(shí)候,很容易對(duì)函數(shù)覆寫。
2,以對(duì)象取代數(shù)據(jù)值
將數(shù)據(jù)項(xiàng)變成對(duì)象
3,將值對(duì)象改為引用對(duì)象
在第二節(jié)中,再增加一個(gè),在多處值對(duì)象中,引用同一個(gè)對(duì)象
4,將引用對(duì)象改為值對(duì)象
和上一節(jié)相反。
5,以對(duì)象取代數(shù)組
這個(gè)很容易理解,特別是數(shù)組中的值含義不同的時(shí)候,用對(duì)象來替換,可以跟清晰的表達(dá)各個(gè)字段的含義。
6,復(fù)制“被監(jiān)視的數(shù)據(jù)”
建立觀察者模式,將數(shù)據(jù)復(fù)制到一個(gè)領(lǐng)域?qū)ο笾小?/p>
7,將單向關(guān)聯(lián)改成雙向關(guān)聯(lián)
8,將雙向關(guān)聯(lián)改成單向關(guān)聯(lián)
9,以字面常量取代魔法數(shù)
代碼中的魔法數(shù),hardcode 都應(yīng)該用常量取代。
10,封裝字段
不直接暴露對(duì)象中的屬性,Java的封裝特征。
11,封裝集合
12,以數(shù)據(jù)類取代記錄
項(xiàng)目中經(jīng)常見到的DTO VO類
13,以類取代類型碼
以新的類替換還數(shù)值類型碼,讓代碼更容易理解。
14,以子類取代類型碼
15,以State Strategy 取代類型碼
用策略模式取代類型碼
16,以字段取代子類
去掉不必要的子類。
這一章同樣是講了很多具體的重構(gòu)手法,還是那句話,需要慢慢在實(shí)際項(xiàng)目中多練習(xí),才能熟練掌握。
第九章,簡化條件表達(dá)式
1,分解條件表達(dá)式。
把表達(dá)式提煉成函數(shù),并取一個(gè)適當(dāng)?shù)拿?,可以讓代碼整潔易懂。
2,合并條件表達(dá)式
很多分開的表達(dá)式可以合并在一起,然后再把合并的表達(dá)式提煉成函數(shù)。每個(gè)過程都可以采用小步前進(jìn),頻繁測試的方式進(jìn)行一步步重構(gòu)。
3,合并重復(fù)的條件表達(dá)式
很多條件里面都有相同的代碼,跟表達(dá)式的結(jié)果沒有關(guān)系,可以放在表達(dá)式外面去做。
4,移除控制標(biāo)記
用break return來取代一些臨時(shí)的控制標(biāo)記。
5,以衛(wèi)語句取代嵌套條件表達(dá)式
通過各種方式,減少嵌套層級(jí)。
6,以多態(tài)取代條件表達(dá)式
這個(gè)也是比較常見的,利用java的多態(tài)特征來處理不同的條件的處理
7,引入Null對(duì)象
我并不是很理解這里,感覺不是很有必要,有其他更好的方式可以替換,特別是在Java8里面。
8,引入斷言
增加斷言,增強(qiáng)代碼可讀性
第十章,簡化函數(shù)調(diào)用
1. Rename Method
要想成為一個(gè)真正的編程高手,起名的水平是至關(guān)重要的。
用現(xiàn)在的工具很容易修改函數(shù)的名稱。
2. Add Parameter
3. Remove Parameter
4. Separete Query from Modifier
rule: 任何有返回值的函數(shù),都不應(yīng)該有看得到的副作用
5. Parameterize Method 令函數(shù)攜帶參數(shù)
用函數(shù)攜帶參數(shù),去掉重復(fù)代碼
6. 以明確函數(shù)取代參數(shù)
去除if switch 判斷,用明確函數(shù)取代參數(shù)
7. 保持對(duì)象完整
不需要從對(duì)象中取多個(gè)值作為參數(shù)傳遞,直接傳遞整個(gè)對(duì)象。
8. 以函數(shù)取代參數(shù)
有些參數(shù)從一個(gè)函數(shù)中取得,然后又傳遞給另外一個(gè)函數(shù),可以在接受參數(shù)的這個(gè)函數(shù)里面直接用函數(shù)的獲取,減少函數(shù)參數(shù)的傳遞。
9. 引入?yún)?shù)對(duì)象
出現(xiàn)多個(gè)可以放在對(duì)象中的參數(shù),把參數(shù)提煉成函數(shù)一個(gè)函數(shù),然后調(diào)用這個(gè)函數(shù)的時(shí)候傳遞對(duì)象,減少函數(shù)調(diào)用參數(shù)的個(gè)數(shù)。
10. 移除設(shè)置函數(shù)
這個(gè)不是很理解,在實(shí)際項(xiàng)目中,對(duì)象中的值經(jīng)常要改變,我們基本都是通過set方法去改變對(duì)象中屬性的值。
11. 隱藏函數(shù)
盡可能降低所有函數(shù)的可見度,易于維護(hù)。
12. 以工廠函數(shù)取代構(gòu)造函數(shù)
13. 封裝向下轉(zhuǎn)型
14. 以異常取代錯(cuò)誤碼
堅(jiān)信:代碼的可理解性應(yīng)該是我們虔誠追求的目標(biāo)
15. 以測試取代異常
個(gè)人不太喜歡拋異常這種做法。
第十一章 處理概況關(guān)系
類的概括關(guān)系即繼承關(guān)系,主要是將函數(shù)上下移動(dòng)于繼承體系之中
1. Pull Up Field
兩個(gè)子類擁有相同的字段,將該字段移動(dòng)至超類。
2. Pull Up Method
函數(shù)在各個(gè)子類中產(chǎn)生完全相同的結(jié)果,移動(dòng)到超類
3. Pull Up Constructor Body
把子類構(gòu)造函數(shù)相同的部分移動(dòng)到超類,在子類構(gòu)造函數(shù)里面調(diào)用super
4. Push Down Method
超類中某個(gè)函數(shù)只和部分子類有關(guān),移動(dòng)到需要的子類中
5. Push Down Field
超類中的某個(gè)字段,只有部分子類用到,移動(dòng)到子類中
6. Extract Subclass
將類中只被某些實(shí)例用到的特性移動(dòng)到子類中
7. Extract Superclass
為兩個(gè)具有相似特性的類建立一個(gè)超類
8. Extract Interface
若干個(gè)類具有相同的接口,把相同的接口的子集提煉到獨(dú)立接口中。
9. Collapse Hierarchy 折疊繼承體系
超類和子類區(qū)別不大,應(yīng)合為一體。
10. Form Template Method 塑造模板函數(shù)
有一些子類,相應(yīng)的某些函數(shù)以相同的順序執(zhí)行類似的操作,每個(gè)操作細(xì)節(jié)不同,將這些操作分別放在獨(dú)立的函數(shù)中,使用相同的方法名,然后在父類中抽象出那些方法。 通過繼承,消除重復(fù)。
11. Replace Inheritance with Delegation 已委托取代繼承
子類只使用超類接口中的一部分,在子類中新建一個(gè)屬性字段保存超類,調(diào)整子類函數(shù),改為委托超類,去掉繼承關(guān)系。
12. Replace Delegation with Inheritance 以繼承取代委托
和上一節(jié)正好相反。
第十二章 大型重構(gòu)
根據(jù)需要安排自己的工作,只在需要添加新功能或者修補(bǔ)錯(cuò)誤時(shí)才進(jìn)行重構(gòu)。不必一開始就完成整個(gè)系統(tǒng)的重構(gòu),重構(gòu)程度只要能滿足其他任務(wù)的需要就行了。應(yīng)該要有持續(xù)而無處不在的重構(gòu)。
1. Tease Apart Inheritance 梳理并分解繼承體系
2. 將過程化設(shè)計(jì)轉(zhuǎn)化為對(duì)象設(shè)計(jì)
3. 將領(lǐng)域和表述/顯示分離
4. 提煉繼承體系
第十三章 重構(gòu),復(fù)用與現(xiàn)實(shí)
為什么不肯重構(gòu)你的程序?
1. 你不知道如何重構(gòu)
2. 如果這些利益是長遠(yuǎn)的,何必現(xiàn)在付出這些努力?長遠(yuǎn)看來,說不定當(dāng)項(xiàng)目收獲這些利益是,自己已經(jīng)不在職位上了
3. 代碼重構(gòu)是一項(xiàng)額外工作,老板付錢給你,主要是讓你編寫新功能
4. 重構(gòu)可能破壞現(xiàn)有程序
第十四章 重構(gòu)工具
Intellij IDEA
第十五章 總結(jié)
沒有最好,只有更好。重構(gòu)應(yīng)該是永無止境的,只有持續(xù)不斷的重構(gòu),才能讓代碼越來越整潔。通過閱讀本書,首先,自己已經(jīng)覺得重構(gòu)是必須的,必要的。第二點(diǎn),關(guān)于具體的重構(gòu)手法,有些是用過的,有些是剛接觸的,需要在實(shí)際運(yùn)用中持續(xù)不斷的學(xué)習(xí)和使用。