//
Martin Fowler,世界級軟件開發(fā)大師,敏捷開發(fā)的開拓者和創(chuàng)始人全球知名的面向?qū)ο蠓治鲈O(shè)計、UML、模式等專業(yè)領(lǐng)域的領(lǐng)頭羊,首創(chuàng)敏捷開發(fā)方法論,被譽(yù)為軟件開發(fā)“教父”,現(xiàn)任職于全球知名技術(shù)咨詢公司ThoughtWorks,首席科學(xué)家。
Martin Fowler
坊間流傳著這樣一句話,如果有一本編程技術(shù)類書籍能夠讓讀者在工作或?qū)嵺`多年后,還在反復(fù)咀嚼玩味、愛不釋手、引導(dǎo)著讀者前進(jìn)著,那個必定是Martin Fowler的《重構(gòu)》。
Martin還是IT領(lǐng)域的知名作家,他撰寫的七本有關(guān)軟件開發(fā)的書廣受程序員們的喜愛,包括重構(gòu)、企業(yè)應(yīng)用程序體系結(jié)構(gòu)模式和UML Distilled等領(lǐng)域。其中《重構(gòu)》風(fēng)靡國內(nèi)外,擁有數(shù)百萬讀者,國內(nèi)豆瓣評分高達(dá)9.5分。
Object Technology International, Inc,Erich Gamma 曾這樣評價Martin:
Martin Fowler清楚地揭示了重構(gòu)過程,他為面向?qū)ο筌浖_發(fā)所做的貢獻(xiàn)難以估量。他解釋了重構(gòu)的原理和最佳實踐,并指出何時何地你應(yīng)該開始挖掘你的代碼以求改善。
因此,我極力推薦你試試Martin Fowler的重構(gòu)手法,你和你的程序都將因此更美好。
軟件開發(fā)“教父”的重構(gòu)生涯
Martin Fowler,1963年出生在英格蘭的沃爾索耳,正是編程世界剛剛起步的年代。
Martin 在80年代初開始接觸軟件行業(yè),那時候Smalltalk還是一門很火的語言,大家都在學(xué)習(xí)這門語言,而Martin也剛開始從事軟件工作——關(guān)于信息系統(tǒng)對象建模方面的顧問。
那個年代沒有任何有關(guān)面向?qū)ο蠓治龊驮O(shè)計的書籍,大家都是用一種簡單的圖符表示法,然后提出一個簡單的建模過程,最后用幾個簡單的示例來加以說明。但Martin認(rèn)為不應(yīng)該把重點(diǎn)放在過程——即如何建模,而是把重點(diǎn)放在過程的結(jié)果——即模型本身,盡管這與當(dāng)時大環(huán)境下的創(chuàng)建方式是相悖的。
到了90年代初,Martin發(fā)現(xiàn),大多數(shù)程序員很難通過緊跟技術(shù)創(chuàng)新的腳步來享受軟件工程領(lǐng)域的新成果。
身為一個信息系統(tǒng)對象建模顧問, Martin從Smalltalk上學(xué)得的專業(yè)知識是遠(yuǎn)遠(yuǎn)超過這個職業(yè)所需要的,他覺得自己既然有了這些建模方面的理念,同時又對編程方面很感興趣,所以他不單在建模方面幫助別人,還在編程方面進(jìn)行指導(dǎo)——借助外力的幫助,精確地運(yùn)用UML。
從建模到編程語言,他把這些都看成一件完整的、而非毫無關(guān)聯(lián)的事。
這樣的工作持續(xù)了近十年,1999年,Martin迎來了他人生中意義重大的一年。
那時Martin造訪了客戶調(diào)研的開發(fā)項目時發(fā)現(xiàn),該系統(tǒng)的核心繼承體系相當(dāng)凌亂,為此,他建議負(fù)責(zé)該項目的經(jīng)理將這些代碼進(jìn)行整理,但是項目經(jīng)理認(rèn)為在項目面臨很大的進(jìn)度壓力下,只要程序看上去還可以運(yùn)行就算是完成。
這種情況下,《重構(gòu):改善既有代碼的設(shè)計》面世了,Martin在這本書中揭示了重構(gòu)的過程,解釋了重構(gòu)的原理和最佳實踐方式,并給出了何時以及何地應(yīng)該開始挖掘代碼以求改善。
重構(gòu)這個理念一經(jīng)推出,受到了廣大程序員的喜愛,他們覺得在不改變代碼外在行為的前提下,對代碼做出修改,以改進(jìn)程序的內(nèi)部結(jié)構(gòu)是一個非常妙的事。后來,這本風(fēng)靡國際IT行業(yè)的《重構(gòu)》被引入國內(nèi),在豆瓣評分以9.2的高分長期霸屏程序員必讀書單中。
2010年,第五屆敏捷軟件開發(fā)大會“敏捷中國2010”上,Martin的“敏捷宣言”又一次引領(lǐng)了整個IT行業(yè)對敏捷開發(fā)的認(rèn)識,從此“敏捷”二字開始風(fēng)行國內(nèi) IT 領(lǐng)域。對程序員們來說,他就是當(dāng)之無愧的先行者。
就在去年,這位先行者又全面升級了《重構(gòu)》,《重構(gòu):改善既有代碼的設(shè)計(第2版)》面世了,國內(nèi)外掀起一陣搶購熱潮,豆瓣評分更是高達(dá)9.5分。
Martin定義的重構(gòu)到底是什么?
說起重構(gòu),還要從1999年發(fā)生在Martin身上的一件事說起。面對凌亂的系統(tǒng)的核心繼承體系,他建議負(fù)責(zé)該項目的經(jīng)理將這些代碼進(jìn)行整理,但是被項目經(jīng)理拒絕。
隨后,Martin把自己的想法第一時間告訴了在這個繼承體系上工作的程序員,程序員都很敏銳, 馬上就看出問題的嚴(yán)重性,特別是在這種需要借助外力才能發(fā)現(xiàn)問題。他們立刻用了兩天的時間整理好這個繼承體系,并刪掉了其中一半代碼,功能毫發(fā)無損,而且發(fā)現(xiàn)在繼承體系中加入新的類或使用系統(tǒng)中的其他類都更快、更容易了,他們十分感謝Martin。
但項目經(jīng)理很不高興,他覺得這些程序員卻白白耗費(fèi)了兩天時間,做的工作卻與未來幾個月要交付的大量功能毫不相干,明明原先的代碼運(yùn)行起來還算正常,為什么要為了可能發(fā)生的問題去花費(fèi)時間提前預(yù)防呢?
不過在6個月之后,這個項目還是宣告失敗了,原因是代碼太復(fù)雜,無法調(diào)試,也無法將性能調(diào)優(yōu)到可接受的水平。再后來,這個項目重新啟動,唯一的解決辦法就是從頭開始編寫整個系統(tǒng),Kent Beck 受邀做了顧問。他做了幾件迥異以往的事,其中最重要的一件就是聽從Martin的建議,堅持以持續(xù)不斷的重構(gòu)行為來整理代碼。
那時,Martin第一次認(rèn)識到重構(gòu)的重要且不可替代性。
“重構(gòu)”這個概念最開始來自于 Smalltalk圈子,由于重構(gòu)是框架開發(fā)中不可缺少的一部分,所以當(dāng)框架設(shè)計者討論自己的工作時,這個術(shù)語就誕生了。
當(dāng)他們精煉自己的類繼承體系時,當(dāng)他們叫喊自己可以拿掉多少多少行代碼時,重構(gòu)的概念慢慢浮出水面,后來重構(gòu)就進(jìn)入了其他編程語言陣營之中。
說得直白一點(diǎn),所謂重構(gòu)(refactoring)就是這樣一個過程:在不改變代碼外在行為的前提下,對代碼做出修改,以改進(jìn)程序的內(nèi)部結(jié)構(gòu)。
重構(gòu)是一種經(jīng)千錘百煉形成的有條不紊的程序整理方法,可以最大限度地減小整理過程中引入錯誤的概率。本質(zhì)上說,重構(gòu)就是在代碼寫好之后改進(jìn)它的設(shè)計。
“在代碼寫好之后改進(jìn)它的設(shè)計”這種說法有點(diǎn)兒奇怪。在軟件開發(fā)的大部分歷史時期,大部分人認(rèn)為,應(yīng)該先設(shè)計而后編碼:首先得有一個良好的設(shè)計,然后才能開始編碼。
但是,隨著時間流逝,人們不斷修改代碼,于是根據(jù)原先設(shè)計所得的系統(tǒng),整體結(jié)構(gòu)逐漸衰弱。代碼質(zhì)量慢慢沉淪,編碼工作從嚴(yán)謹(jǐn)?shù)墓こ虊櫬錇楹硜y劈的隨性行為。
而“重構(gòu)”正好與此相反。
哪怕手上有一個糟糕的設(shè)計,甚至是一堆混亂的代碼,我們也可以借由重構(gòu)將它加工成設(shè)計良好的代碼。重構(gòu)的每個步驟都很簡單,甚至顯得有些過于簡單:只需要把某個字段從一個類移到另一個類,把某些代碼從一個函數(shù)拉出來構(gòu)成另一個函數(shù),或是在繼承體系中把某些代碼推上推下就行了。
但是,聚沙成塔,這些小小的修改累積起來就可以根本改善設(shè)計質(zhì)量。這和一般常見的“軟件會慢慢腐爛”的觀點(diǎn)恰恰相反。
有了重構(gòu)以后,Martin發(fā)現(xiàn)工作的平衡點(diǎn)開始發(fā)生變化,例如,設(shè)計不是在一開始完成的,而是在整個開發(fā)過程中逐漸浮現(xiàn)出來。在系統(tǒng)構(gòu)筑過程中,他學(xué)會了如何不斷改進(jìn)設(shè)計。這個“構(gòu)筑-設(shè)計”的反復(fù)互動,可以讓一個程序在開發(fā)過程中持續(xù)保有良好的設(shè)計。
Martin認(rèn)為,重構(gòu)這東西不可能一開始就完全正確,它將隨著設(shè)計者的經(jīng)驗成長而進(jìn)化,代碼被閱讀和被修改的次數(shù)遠(yuǎn)遠(yuǎn)多于它被編寫的次數(shù)。而保持代碼易讀、易修改的關(guān)鍵,就是重構(gòu)。
作為一本為專業(yè)程序員編寫的重構(gòu)指南——《重構(gòu)》,Martin的目的是告訴所有程序員如何以一種可控且高效的方式進(jìn)行重構(gòu),從而減少了開發(fā)過程中的風(fēng)險。
書里提出的重構(gòu)準(zhǔn)則將幫助他們學(xué)習(xí)如何有條不紊地、一次一小步地修改代碼、改進(jìn)程序結(jié)構(gòu),且不會引入錯誤的正確的重構(gòu)方式,最終得到有效的、長期可運(yùn)行的代碼程序,而不是去遵循那句古老的工程諺語:“如果它還可以運(yùn)行,就不要動它?!?/span>
重構(gòu)再度升級
《重構(gòu)》自1999年面世以來,已經(jīng)經(jīng)過21年了。軟件行業(yè)里新的編程語言不斷涌現(xiàn),老的編程語言也加快迭代,而函數(shù)式編程和面向?qū)ο笠粯映闪酥髁骶幊陶Z言的標(biāo)配。不僅軟件開發(fā)技術(shù)發(fā)生了很多重要的變化,各種軟件開發(fā)工具也日益現(xiàn)代化,對開發(fā)的更好支持也已成為主流編程語言新的核心競爭力。
而現(xiàn)在, “重構(gòu)”這一理念已被讀者廣泛接納,作為一種經(jīng)千錘百煉形成的有條不紊的程序整理方法,能夠最大限度地減小整理過程中引入錯誤的概率,成為編程的詞匯表中不可或缺的部分,《重構(gòu)》一書至今依舊被無數(shù)程序員奉為軟件開發(fā)領(lǐng)域的經(jīng)典之作。
但重構(gòu)的扎實功夫要學(xué)起來、做起來,頗不是件輕松的事,且不說詳盡到近乎瑣碎的重構(gòu)手法,光是單元測試一事,怕是已有九成程序員無法企及,漸漸地,“重構(gòu)”成了一塊漂亮的招牌,大家都愿意掛上這個名號,可實際上干的卻多是“刀劈斧砍”的勾當(dāng)。
就國內(nèi)先如今的情況而論,“重構(gòu)”概念的表里分離,大有愈演愈烈之勢。隨著當(dāng)年的一線技術(shù)人員紛紛走上領(lǐng)導(dǎo)崗位,他們樂于將“重構(gòu)”這塊漂亮招牌用在更寬泛的環(huán)境下,例如系統(tǒng)架構(gòu)乃至組織結(jié)構(gòu),都可以“重構(gòu)”一下。
然而基本功的欠缺,卻也一路如影隨形。當(dāng)年在對象中的刀劈斧砍,如今被照搬到了架構(gòu)、組織的調(diào)整。于是“重構(gòu)”的痛苦回憶又一遍遍重演,甚而程度更深、影響更廣、危害更烈。
通過重構(gòu),現(xiàn)在的程序員普遍通過微增量來開發(fā)系統(tǒng)、編寫測試用例。但Martin認(rèn)為這遠(yuǎn)遠(yuǎn)不夠,重構(gòu)的影響其實應(yīng)該更廣泛,20年前,他和其他先行者鼓勵大家都接受重構(gòu)這一概念,但是,在整個行業(yè)普及重構(gòu)仍需要相當(dāng)長的時間。
對于IT領(lǐng)域來說,Martin不僅僅是一個先行者,他還是一個引路者。
Martin希望看到更多人使用他們大力推廣的測試法,使用持續(xù)集成和持續(xù)交付等方法。但涉及上述概念,Martin覺得自己只能盡量在書中,從主客觀上盡可能詳盡地解釋這些技術(shù),并希望這會說服更多人進(jìn)行嘗試,“當(dāng)他們嘗到甜頭后就會在工作中真正用上這些方法“。
針對這一現(xiàn)象,Martin推出了《重構(gòu):改善既有代碼的設(shè)計(第2版)》,他在第1版的基礎(chǔ)上做了全面修訂,反映了編程領(lǐng)域業(yè)20年來發(fā)生的許多變化,但Martin傳遞的理念也始終如一:不改變外在行為,而提高代碼質(zhì)量,但將基礎(chǔ)功夫做得更扎實。讓人不禁感嘆于他對“微末功夫”的執(zhí)著!
很多人好奇Martin為什么決定將《重構(gòu)》再版?Martin給出了3個原因:
第1版里的代碼已經(jīng)很陳舊了,書里面還有Java.util.Vector;
一些重構(gòu)并非是與面向?qū)ο缶o耦合在一起的;
Martin認(rèn)為Java是一種非常嚴(yán)格的面向?qū)ο缶幊陶Z言,而第1版中所有的重構(gòu)都是基于面向?qū)ο蟮模胪ㄟ^再版來說明每個程序員都可以用任何(編程)語言、在任何環(huán)境中、遵循書中提到的范例進(jìn)行重構(gòu),這也是他決定再版《重構(gòu)》的源動力。
Martin認(rèn)為重構(gòu)的基本機(jī)制不會發(fā)生巨大轉(zhuǎn)變,即使使用面向?qū)ο笳Z言工作也是如此。他覺得有一等函數(shù)很好,可以嘗試許多函數(shù)式的理念,例如,寫軟件時大部分函數(shù)都具備引用透明性,這在面向?qū)ο笙到y(tǒng)和在函數(shù)式系統(tǒng)中都是好事。
所以Martin不像許多人那樣在函數(shù)式編程和面向?qū)ο缶幊涕g畫出明確界限,他認(rèn)為兩者有很多共同的地方,未必存在巨大差異。他鼓勵程序員不要有門戶之見,要用綜合理念來解決問題。
對于最新版《重構(gòu)》,Martin認(rèn)為有一個重構(gòu)手法也許值得注意,拆分階段(split phase)。早在幾年前,當(dāng)Martin和Ken Beck商談拆分階段的時候,Martin第一次意識到這也是重構(gòu)手法。
通過這種重構(gòu)手法,Martin將大量計算合理分為兩個階段,使用中間數(shù)據(jù)結(jié)構(gòu)進(jìn)行通信。其中一個例子是解析拆分階段可有效地將token從解析中抽離出來,這樣就得到了一個可可保存在存儲器種的token流,可處理相關(guān)字符串、文本字符串。
而說到重構(gòu)工具,Martin和Kent做了很多年了,但他們從來沒有意識到工具的重要性;當(dāng)他們意識到這一點(diǎn)之后,他們感覺重構(gòu)工具已經(jīng)無處不在,他們認(rèn)為這個內(nèi)容非常重要,然后Martin就真把這些新內(nèi)容加到新版中了。
重構(gòu)的關(guān)鍵是理念:通過進(jìn)行最細(xì)微的改變,然后將這些變化串聯(lián)起來,這就是重構(gòu)思維核心。將一個大變化拆分為許多小變化,又在盡可能多進(jìn)行細(xì)微變化的同時,不改變系統(tǒng)的整體表現(xiàn),然后隨時間推移,反復(fù)練習(xí)并思考如何進(jìn)行拆分。Martin在《重構(gòu) 2》一書中說過,他通過重構(gòu)框架思考問題的體驗,嘗試各種高效的重構(gòu)手法并做出決定,最重要的就是通過實踐進(jìn)行重構(gòu)。
他認(rèn)為在嘗試了不同的重構(gòu)手法后,找出能重構(gòu)手法生成理想序列,繼而進(jìn)行嘗試識別出這種重構(gòu)手法,而同樣的邏輯也適用于更廣泛的層面。
因此,他采用了70多個種可行的重構(gòu),并且把每個重構(gòu)都介紹了一種經(jīng)過驗證的代碼變換手法的動機(jī)和技術(shù)。就像軟件開發(fā)的大多數(shù)工作一樣,重構(gòu)除了動手做,別無他法,必須反復(fù)實踐,必須在項目中使用重構(gòu)。
Martin始終希望,重構(gòu)準(zhǔn)則能幫助大家一步步修改自己的代碼,減少了開發(fā)過程中的風(fēng)險。
重構(gòu)=編碼
在日新月異的 IT 技術(shù)世界里,不變的東西其實還是有的——重構(gòu)。
從《重構(gòu)》的英文原版引進(jìn)國內(nèi),到現(xiàn)在已經(jīng)過去20年了。Martin Fowler 這次對本書進(jìn)行的重構(gòu),體現(xiàn)了近年來編程領(lǐng)域的一些思潮變化,既有設(shè)計,又永遠(yuǎn)有改進(jìn)空間。
盡管時間是最強(qiáng)大的重構(gòu)工具,連書里的示例語言都從 Java 變成 JavaScript 了,但書中的理念和實踐的價值并沒有隨時間流逝。
重構(gòu)早就成了軟件開發(fā)從業(yè)者本能的一部分,每個 IDE 都內(nèi)置了重構(gòu)功能,每個程序員都定期重構(gòu)自己的代碼。
對于軟件工程師來說,重構(gòu),并不是額外的工作,它就是編碼本身。切實地讀懂了《重構(gòu)》的軟件工程師,在能力上都會獲得一個數(shù)量級的提升。