重構:改善既有程式碼的設計(評註版) 評註者序

broadviewbj發表於2011-07-22

重構:改善既有程式碼的設計(評註版) 評註者序

 

評註者序

近十年來,若要討論如何改進程式碼的質量,很難繞過Martin Fowler的這本經典著作。這本書已經影響了幾代程式設計師,或許會持續不斷地影響未來的一批程式設計師。遺憾的是,在現實中我們仍然看到了重構的步履維艱。一方面是因為程式設計師的重構技巧還有待磨礪,另一方面則是因為專案的壓力,使得我們往往將重構視為雞肋。

重構是可有可無的嗎?Practices of an Agile Developer的作者Andy Hunt認為要投入時間和精力保持程式碼的整潔、敞亮。在專案中,程式碼應該是亮堂的,不應該有黑暗死角。相信維護過遺留程式碼的程式設計師,在面對糾纏不清、複雜混亂的程式碼時,對於此言必有心有慼慼焉之感。重構是清理程式碼垃圾的有效手段,它有助於營造明晰的程式結構、一致的程式碼風格、有效的職責重用,能夠恰如其分地在簡單與複雜之間尋覓到程式碼之美,合理地權衡程式碼質量與開發效率,從而提升至優雅編碼的境界。

如何呈現重構之美?關鍵在於發現醜陋而不堪忍受。對醜的憎惡實則是一種強悍的驅動力,它會驅使你運用重構,直到重構變成如呼吸一般自然而不可缺失。重構無須專門的階段,隨時隨地皆可進行。經常的重構可以保證程式碼常拭常新,如利刃一般鋒利。那種為了單一地追求開發速度,而置程式碼質量於不顧的做法,實則是殺雞取卵,可以預期的結果就是系統的破窗戶逐漸蔓延,最後落得不可收拾的下場。重構並非不可執行,關鍵還在於我們對於重構的態度和運用重構的習慣。必須在思想上認同重構的重要性;然後在技巧上不斷提升重構技能,並輔以對重構工具的使用,就能最大程度地放大重構在軟體開發中積極的一面。

若要提升重構技能,閱讀本書就是必須,沒有例外。那麼,對於這樣的經典之作而言,還有點評的必要嗎?是否我們在做著畫蛇添足的蠢事?Martin Fowler已經做得足夠好,任何點評都是一種饒舌?不盡然!身為點評者的我,如果僅滿足於在本書的邊邊角角上,不痛不癢地發表不承擔責任的語氣詞,那麼對於這種點評,不要也罷。我在著手本書的點評工作時,對自己的定位是:我不是點評者,我是創作者。在點評本書的過程中,我是以創作自己著作的態度來完成的。

本書的大多數點評內容,並非片言隻語,很多內容都是自己重構心得以及重構技巧的運用。我閱讀參考了大量的書籍,例如《程式設計師修煉之道》、《重構與模式》、《領域驅動設計》、《反模式》、《軟體架構的藝術》、《修改程式碼的藝術》、《程式碼整潔之道》等十餘部相關書籍。畢竟站在軟體世界的角度來看,Martin Fowler寫作本書的時代已經相當古老了。在這之後,產生了許多精彩的設計技巧、重構理念與方法。譬如在與重構相關的內容中,本書未曾論述的就包括:架構重構、介面重構、資料庫重構、重構模式等內容。在點評過程中,我希望能以開放的方式描述自己閱讀本書的感受,講述自己重構的體驗,並在能力範圍之內給出自己的一些意見與看法。

我作為一名程式設計師,看到了太多漠視或者無視垃圾程式碼的同行們,這其中也包括曾經的我。但當我深陷遺留程式碼的痛苦時,對於這樣的現狀就變得痛心疾首了。近年來,軟體業界對於軟體工藝以及敏捷方法的推行,在一定程度上改善了人們的看法,但在實際推行中依舊困難重重。最大的起因就在於我們將程式設計師定位為程式碼工人,認為編碼工作是低端程式設計師的責任。我們妄圖創造如機械製造行業一般的軟體工廠,卻忽略了軟體程式設計實則還有藝術的一面。我始終認為,程式碼仍然是架構的一部分,程式碼質量決定了架構質量,而重構則是延緩系統衰老的生命源泉。正所謂千里之行始於足下,就讓重構之行,始於本書吧。

 

 

重構這個概念來自Smalltalk圈子,沒多久就進入了其他語言陣營之中。由於重構是框架開發中不可缺少的一部分,所以當框架開發人員討論自己的工作時,這個術語就誕生了。當他們精練自己的類繼承體系時,當他們叫喊自己可以拿掉多少多少行程式碼時,重構的概念慢慢浮出水面。框架設計者知道,這東西不可能一開始就完全正確,它將隨著設計者的經驗成長而進化;他們也知道,程式碼被閱讀和被修改的次數遠遠多於它被編寫的次數。保持程式碼易讀、易修改的關鍵,就是重構——對框架而言如此,對一般軟體也如此。

好極了,還有什麼問題嗎?問題很顯然:重構具有風險。它必須修改運作中的程式,這可能引入一些不易察覺的錯誤。如果重構方式不恰當,可能毀掉你數天甚至數星期的成果。如果重構時不做好準備,不遵守規則,風險就更大。你挖掘自己的程式碼,很快發現了一些值得修改的地方,於是你挖得更深。挖得越深,找到的重構機會就越多,於是你的修改也越多……最後你給自己挖了個大坑,卻爬不出去了。為了避免自掘墳墓,重構必須系統化進行。我在《設計模式》書中和另外三位作者曾經提過:設計模式為重構提供了目標。然而確定目標只是問題的一部分而已,改造程式以達到目標是另一個難題。

Martin Fowler和本書另幾位作者清楚揭示了重構過程,他們為物件導向軟體開發所做的貢獻難以衡量。本書解釋了重構的原理和最佳實踐,並指出何時何地你應該開始挖掘你的程式碼以求改善。本書的核心是一系列完整的重構方法,其中每一項都介紹一種經過實踐檢驗的程式碼變換手法的動機和技術。某些專案如Extract MethodMove Field看起來可能很淺顯,但不要掉以輕心,因為理解這類技術正是有條不紊地進行重構的關鍵。本書所提的這些重構手法將幫助你一次一小步地修改你的程式碼,這就減少了過程中的風險。很快你就會把這些重構手法和其名稱加入自己的開發詞典中,並且朗朗上口。

我第一次體驗有講究的、一次一小步的重構,是某次與Kent Beck30 000英尺高空的飛行旅途中結對程式設計。我們運用本書收錄的重構手法,保證每次只走一步。最後,我對這種實踐方式的效果感到十分驚訝。我不但對最後結果更有信心,而且開發壓力也小了很多。所以,我極力推薦你試試這些重構手法,你和你的程式都將因此更美好。

 

Erich Gamma

《設計模式》第一作者,Eclipse平臺主架構師

熊節 

 

從前,有位諮詢顧問造訪客戶調研其開發專案。系統核心是個類繼承體系,顧問看了開發人員所寫的一些程式碼。他發現整個體系相當凌亂,上層超類對於系統的運作做了一些假設,下層子類實現這些假設。但是這些假設並不適合所有子類,導致覆寫(override)工作非常繁重。只要在超類做點修改,就可以減少許多覆寫工作。在另一些地方,超類的某些意圖並未被良好理解,因此其中某些行為在子類內重複出現。還有一些地方,好幾個子類做相同的事情,其實可以把它們搬到繼承體系的上層去做。

這位顧問於是建議專案經理看看這些程式碼,把它們整理一下,但是經理並不熱衷於此,畢竟程式看上去還可以執行,而且專案面臨很大的進度壓力。於是經理說,晚些時候再抽時間做這些整理工作。

顧問也把他的想法告訴了在這個繼承體系上工作的程式設計師,告訴他們可能發生的事情。程式設計師都很敏銳,馬上就看出問題的嚴重性。他們知道這並不全是他們的錯,有時候的確需要藉助外力才能發現問題。程式設計師立刻用了一兩天的時間整理好這個繼承體系,並刪掉了其中一半程式碼,功能毫髮無損。他們對此十分滿意,而且發現在繼承體系中加入新的類或使用系統中的其他類都更快、更容易了。

專案經理並不高興。進度排得很緊,有許多工作要做。系統必須在幾個月之後釋出,而這些程式設計師卻白白耗費了兩天時間,乾的工作與要交付的多數功能毫無關係。原先的程式碼執行起來還算正常,他們的新設計看來有點過於追求完美。專案要交付給客戶的,是可以有效執行的程式碼,不是用以取悅學究的完美東西。顧問接下來又建議應該在系統的其他核心部分進行這樣的整理工作,這會使整個專案停頓一至兩個星期。所有這些工作只是為了讓程式碼看起來更漂亮,並不能給系統新增任何新功能。

你對這個故事有什麼感想?你認為這個顧問的建議(更進一步整理程式)是對的嗎?你會遵循那句古老的工程諺語嗎:如果它還可以執行,就不要動它。

我必須承認自己有某些偏見,因為我就是那個顧問。六個月之後這個專案宣告失敗,很大的原因是程式碼太複雜,無法除錯,也無法獲得可被接受的效能。

後來,專案重新啟動,幾乎從頭開始編寫整個系統,Kent Beck受邀做了顧問。他做了幾件迥異以往的事,其中最重要的一件就是堅持以持續不斷的重構行為來整理程式碼。這個專案的成功,以及重構在這個成功專案中扮演的角色,啟發了我寫這本書,如此一來我就能夠把Kent和其他一些人已經學會的以重構方式改進軟體質量的知識,傳播給所有讀者。

什麼是重構

所謂重構(refactoring)是這樣一個過程:在不改變程式碼外在行為的前提下,對程式碼做出修改,以改程式序的內部結構。重構是一種經千錘百煉形成的有條不紊的程式整理方法,可以最大限度地減少整理過程中引入錯誤的機率。本質上說,重構就是在程式碼寫好之後改進它的設計。

在程式碼寫好之後改進它的設計這種說法有點兒奇怪。按照目前對軟體開發的理解,我們相信應該先設計而後編碼:首先得有一個良好的設計,然後才能開始編碼。但是,隨著時間流逝,人們不斷修改程式碼,於是根據原先設計所得的系統,整體結構逐漸衰弱。程式碼質量慢慢沉淪,編碼工作從嚴謹的工程墮落為胡砍亂劈的隨性行為。

重構正好與此相反。哪怕你手上有一個糟糕的設計,甚至是一堆混亂的程式碼,你也可以藉由重構將它加工成設計良好的程式碼。重構的每個步驟都很簡單,甚至顯得有些過於簡單:你只需要把某個欄位從一個類移到另一個類,把某些程式碼從一個函式拉出來構成另一個函式,或是在繼承體系中把某些程式碼推上推下就行了。但是,聚沙成塔,這些小小的修改累積起來就可以根本改善設計質量。這和一般常見的軟體會慢慢腐爛的觀點恰恰相反。

透過重構,你可以找出改變的平衡點。你會發現所謂設計不再是一切動作的前提,而是在整個開發過程中逐漸浮現出來。在系統構築過程中,你可以學習如何強化設計,其間帶來的互動可以讓一個程式在開發過程中持續保有良好的設計。

本書有什麼

本書是一本為專業程式設計師而寫的重構指南。我的目的是告訴你如何以一種可控制且高效率的方式進行重構。你將學會如何有條不紊地改程式序結構,而且不會引入錯誤,這就是正確的重構方式。

按照傳統,圖書應該以引言開頭。儘管我也同意這個原則,但是我發現以概括性的討論或定義來介紹重構,實在不是一件容易的事。所以我決定用一個例項做為開路先鋒。第1章展示了一個小程式,其中有些常見的設計缺陷,我把它重構為更合格的物件導向程式。其間我們可以看到重構的過程,以及幾個很有用的重構手法。如果你想知道重構到底是怎麼回事兒,這一章不可不讀。

2章討論重構的一般性原則、定義,以及進行重構的原因,我也大致介紹了重構所存在的一些問題。第3章由Kent Beck介紹如何嗅出程式碼中的壞味道,以及如何運用重構清除這些壞味道。測試在重構中扮演著非常重要的角色,第4章介紹如何運用一個簡單而且開源的Java測試框架,在程式碼中構築測試環境。

本書的核心部分——重構列表——從第5章延伸至第12章。它不能說是一份全面的列表,只是一個起步,其中包括迄今為止我在工作中整理下來的所有重構手法。每當我想做點什麼——例如Replace Conditional with Polymorphism 245)的時候,這份列表就會提醒我如何一步一步安全前進。我希望這是值得你日後一再回顧的部分。

本書介紹了其他人的許多研究成果,最後幾章就是由他們之中的幾位客串所寫的。Bill Opdyke在第13章記述他將重構技術應用於商業開發過程中遇到的一些問題。Don RobertsJohn Brant在第14章展望重構技術的未來——自動化工具。我把最後一章(第15章)留給重構技術的頂尖大師Kent Beck來壓軸。

Java中運用重構

本書範例全部使用Java撰寫。重構當然也可以在其他語言中實現,而且我也希望這本書能夠給其他語言使用者帶來幫助。但我覺得我最好在本書中只使用Java,因為那是我最熟悉的語言。我會不時寫下一些提示,告訴讀者如何在其他語言中進行重構,不過我真心希望看到其他人在本書的基礎上針對其他語言寫出更多重構方面的書籍。

為了很好地與讀者交流我的想法,我沒有使用Java語言中特別複雜的部分。所以我避免使用內嵌類、反射機制、執行緒以及很多強大的Java特性。這是因為我希望儘可能清楚地展現重構的核心。

我應該提醒你,這些重構手法並不針對併發或分散式程式設計。那些主題會引出更多的考慮,本書並未涉及。

誰該閱讀本書

本書的目標讀者是專業程式設計師,也就是那些以編寫軟體為生的人。書中的示例和討論,涉及大量需要詳細閱讀和理解的程式碼。這些例子都以Java寫成。之所以選擇Java,因為它是一種應用範圍越來越廣的語言,而且任何具備C語言背景的人都可以輕易理解它。Java是一種面嚮物件語言,而物件導向機制對於重構有很大幫助。

儘管關注物件是程式碼,但重構對於系統設計也有巨大影響。資深設計師和架構師也很有必要了解重構原理,並在自己的專案中運用重構技術。最好是由老資格、經驗豐富的開發人員來引入重構技術,因為這樣的人最能夠透徹理解重構背後的原理,並根據情況加以調整,使之適用於特定工作領域。如果你使用的不是Java,這一點尤其重要,因為你必須把我給出的範例以其他語言改寫。

下面我要告訴你,如何能夠在不通讀全書的情況下充分用好它。

q 如果你想知道重構是什麼,請閱讀第1章,其中示例會讓你清楚重構的過程。

q 如果你想知道為什麼應該重構,請閱讀第12章。它們告訴你重構是什麼以及為什麼應該重構。

q 如果你想知道該在什麼地方重構,請閱讀第3章。它會告訴你一些程式碼特徵,這些特徵指出這裡需要重構

q 如果你想著手進行重構,請完整閱讀第14章,然後選擇性地閱讀重構列表。一開始只需概略瀏覽列表,看看其中有些什麼,不必理解所有細節。一旦真正需要實施某個準則,再詳細閱讀它,從中獲取幫助。列表部分是供查閱的參考性內容,你不必一次就把它全部讀完。此外你還應該讀一讀列表之後其他作者的客串章節,特別是第15章。

站在前人的肩膀上

就在本書一開始的此時此刻,我必須說:這本書讓我欠了一大筆人情債,欠那些在過去十年中做了大量研究工作並開創重構領域的人一大筆債。這本書原本應該由他們之中的某個人來寫,但最後卻是由我這個有時間有精力的人撿了便宜。

重構技術的兩位最早倡導者是Ward CunninghamKent Beck。他們很早就把重構作為開發過程的一個核心成分,並且在自己的開發過程中運用它。尤其需要說明的是,正因為和Kent的合作,才讓我真正看到了重構的重要性,並直接激勵了我寫出這本書。

Ralph JohnsonUIUC(伊利諾伊大學厄巴納尚佩恩分校)領導了一個小組,這個小組因其在物件技術方面的實際貢獻而聲名遠揚。Ralph很早就是重構技術的擁護者,他的一些學生也一直在研究這個課題。Bill Opdyke的博士論文是重構研究的第一份詳細的書面成果。John BrantDon Roberts則早已不滿足於寫文章了,他們寫了一個工具叫Refactoring Browser(重構瀏覽器),對Smalltalk程式實施重構工程。

致謝

儘管有這些研究成果可以借鑑,我還是需要很多協助才能寫出這本書。首先,並且也是最重要的,Kent Beck給了我巨大的幫助。Kent在底特律的某個酒吧和我談起他正在為Smalltalk Report撰寫一篇論文[Beckhanoi],從此播下本書的第一顆種子。那次談話不但讓我開始注意到重構技術,而且我還從中了許多想法放到本書第1章。Kent也在其他很多方面幫助我,想出程式碼味道這個概念的是他,當我遇到各種困難時,鼓勵我的人也是他,常常和我一起工作助我完成這本書的,還是他。我常常忍不住這麼想:他完全可以自己把這本書寫得更好。可惜有時間寫書的人是我,所以我也只能希望自己不要做得太差。

寫這本書的時候,我希望能把一些專家經驗直接與你分享,所以我非常感激那些花時間為本書添磚加瓦的人。Kent BeckJohn BrantWilliam OpdykeDon Roberts編撰或合寫了本書部分章節。此外Rich GarzanitiRon Jeffries幫我新增了一些有用的文中註解。

在任何一本此類技術書裡,作者都會告訴你,技術審閱者提供了巨大的幫助。一如既往,Addison-Wesley出版社的Carter Shanklin和他的團隊組織了強大的審稿人陣容,他們是:

q Ken AuerRolemodel軟體公司

q Joshua BlochSun公司Java軟體部

q John BrantUIUC

q Scott CorleyHigh Voltage軟體公司

q Ward CunninghamCunninghamCunningham公司

q Stéphane Ducasse

q Erich Gamma,物件技術國際公司

q Ron Jeffries

q Ralph Johnson,伊利諾伊大學

q Joshua KerievskyIndustrial Logic公司

q Doug Lea,紐約州立大學Oswego分校

q Sander Tichelaar

他們大大提高了本書的可讀性和準確性,並且至少去掉了一些任何手稿都可能會藏有的錯誤。在此我要特別感謝兩個效果顯著的建議,它們讓我的書看上去耳目一新:WardRon建議我以重構前後效果並列對照的方式寫第1章,Joshua Kerievsky建議我在重構列表中畫出程式碼草圖。

除了正式審閱小組,還有很多非正式的審閱者。這些人或看過我的手稿,或關注我的網頁並留下對我很有幫助的意見。他們是Leif BennettMichael FeathersMichael FinneyNeil GalarneauHisham GhazouliTony GouldJohn IsnerBrian MarickRalf ReissingJohn SaltMark SwansonDave ThomasDon Wells。我相信肯定還有一些被我遺忘的人,請容我在此向你們道歉,並致上我的謝意。

有一個特別有趣的審閱小組,就是惡名昭彰UIUC讀書小組。本書反映出他們的眾多研究成果,我要特別感謝他們用錄音記錄的意見。這個小組成員包括Fredrico“Fred”BalaguerJohn BrantIan ChaiBrian FooteAlejandra GarridoZhijiang“John”HanPeter HatchRalph JohnsonSongyu“Raymond”LuDragos-Anton ManolescuHiroaki NakamuraJames OverturfDon RobertsChieko ShiraiLes TyrellJoe Yoder

任何好想法都需要在嚴酷的生產環境中接受檢驗。我看到重構對於克萊斯勒綜合薪資系統(Chrysler Comprehensive CompensationC3)發揮了巨大的作用。我要感謝那個團隊的所有成員:Ann AndersonEd AnderiRalph BeattieKent BeckDavid BryantBob CoeMarie DeArmentMargaret FronczakRich GarzanitiDennis GoreBrian HackerChet HendricksonRon JeffriesDoug JoppieDavid KimPaul KowalskyDebbie MuellerTom MuraskyRichard NutterAdrian PanteaMatt SaigeonDon ThomasDon Wells。和他們一起工作所獲得的第一手資料,鞏固了我對重構原理和作用的認識。他們使用重構技術所取得的進步極大程度地幫助我看到:重構技術應用於歷時多年的大型專案中,可以起到何等的作用!

再提一句,我得到了Addison-Wesley出版社的JCarter Shanklin及其團隊的幫助,包括Krysia BebickSusan CestoneChuck DuttonKristin EricksonJohn FullerChristopher GuzikowskiSimone PaymentGenevieve Rajewski。與優秀出版商合作是一個令人愉快的經歷,他們為我提供了大量的支援和幫助。

談到支援,為一本書付出最多的,總是距離作者最近的人。那就是現在已成為我妻子的Cindy。感謝她,當我埋首工作的時候,還是一樣愛我。即使在我投入寫書時,也總會不斷想起她。

 

Martin Fowler

於馬薩諸塞州Melrose

fowler @acm.org

熊節 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/13164110/viewspace-702860/,如需轉載,請註明出處,否則將追究法律責任。

相關文章