From: http://www.cnblogs.com/lane_cn/archive/2006/02/05/325782.html
什么是重構(gòu)
重構(gòu),用最簡(jiǎn)單的一句話說(shuō):就是要在不改變系統(tǒng)功能的情況下,對(duì)系統(tǒng)的內(nèi)部結(jié)構(gòu)進(jìn)行重新調(diào)整。重構(gòu)的最直接目的在于改進(jìn)軟件系統(tǒng)的內(nèi)部架構(gòu)。一個(gè)好的結(jié)構(gòu)可以更加適應(yīng)于需求的變化,更好的滿足客戶的需求,最大限度的延長(zhǎng)軟件系統(tǒng)的生命周期。
為什么要重構(gòu)
在不改變系統(tǒng)功能的情況下,改變系統(tǒng)的實(shí)現(xiàn)方式。為什么要這么做?投入精力不用來(lái)滿足客戶關(guān)心的需求,而是僅僅改變了軟件的實(shí)現(xiàn)方式,這是否是在浪費(fèi)客戶的投資呢?
重構(gòu)的重要性要從軟件的生命周期說(shuō)起。軟件不同與普通的產(chǎn)品,他是一種智力產(chǎn)品,沒(méi)有具體的物理形態(tài)。一個(gè)軟件不可能發(fā)生物理?yè)p耗,界面上的按鈕永遠(yuǎn)不會(huì)因?yàn)榘磩?dòng)次數(shù)太多而發(fā)生接觸不良。那么為什么一個(gè)軟件制造出來(lái)以后,卻不能永遠(yuǎn)使用下去呢?
對(duì)軟件的生命造成威脅的因素只有一個(gè):需求的變更。一個(gè)軟件總是為解決某種特定的需求而產(chǎn)生,時(shí)代在發(fā)展,客戶的業(yè)務(wù)也在發(fā)生變化。有的需求相對(duì)穩(wěn)定一些,有的需求變化的比較劇烈,還有的需求已經(jīng)消失了,或者轉(zhuǎn)化成了別的需求。在這種情況下,軟件必須相應(yīng)的改變。
考慮到成本和時(shí)間等因素,當(dāng)然不是所有的需求變化都要在軟件系統(tǒng)中實(shí)現(xiàn)。但是總的說(shuō)來(lái),軟件要適應(yīng)需求的變化,以保持自己的生命力。
這就產(chǎn)生了一種糟糕的現(xiàn)象:軟件產(chǎn)品最初制造出來(lái),是經(jīng)過(guò)精心的設(shè)計(jì),具有良好架構(gòu)的。但是隨著時(shí)間的發(fā)展、需求的變化,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實(shí)現(xiàn)變更,不可避免的要違反最初的設(shè)計(jì)構(gòu)架。經(jīng)過(guò)一段時(shí)間以后,軟件的架構(gòu)就千瘡百孔了。bug越來(lái)越多,越來(lái)越難維護(hù),新的需求越來(lái)越難實(shí)現(xiàn),軟件的構(gòu)架對(duì)新的需求漸漸的失去支持能力,而是成為一種制約。最后新需求的開發(fā)成本會(huì)超過(guò)開發(fā)一個(gè)新的軟件的成本,這就是這個(gè)軟件系統(tǒng)的生命走到盡頭的時(shí)候。
重構(gòu)就能夠最大限度的避免這樣一種現(xiàn)象。系統(tǒng)發(fā)展到一定階段后,使用重構(gòu)的方式,不改變系統(tǒng)的外部功能,只對(duì)內(nèi)部的結(jié)構(gòu)進(jìn)行重新的整理。通過(guò)重構(gòu),不斷的調(diào)整系統(tǒng)的結(jié)構(gòu),使系統(tǒng)對(duì)于需求的變更始終具有較強(qiáng)的適應(yīng)能力。
拒絕變化 VS 擁抱變化
按照傳統(tǒng)的軟件設(shè)計(jì)方式,軟件的生產(chǎn)分為需求調(diào)查、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼、單體測(cè)試、聯(lián)合測(cè)試、現(xiàn)場(chǎng)部署幾個(gè)階段。雖說(shuō)這幾個(gè)階段是可以互相滲透,但是總的來(lái)說(shuō)是有一定次序的,前一個(gè)階段的工作是后一個(gè)階段工作的基礎(chǔ)。這就向下面這樣一種V形的模式:

往下的方向?qū)⑾到y(tǒng)進(jìn)行分解,往上的方向?qū)⑾到y(tǒng)進(jìn)行整合。這樣的開發(fā)形式將軟件開發(fā)分為設(shè)計(jì)前和設(shè)計(jì)后兩個(gè)階段,開發(fā)過(guò)程中存在一個(gè)重要的“里程碑”——設(shè)計(jì)說(shuō)明書的。在設(shè)計(jì)說(shuō)明書完成前,工程處于“設(shè)計(jì)”階段,而在設(shè)計(jì)說(shuō)明書完成之后,工程則進(jìn)入“實(shí)施”階段。一旦到了實(shí)施階段,任何需求或者設(shè)計(jì)上的變更都是非常困難的,需要花費(fèi)大量的成本。通常為了保證工程的順利實(shí)施,開發(fā)人員常有這樣一種沖動(dòng):按住客戶的手,在需求說(shuō)明書上簽字。并且告訴客戶:“從今天開始,任何需求變更都要停止,直到我們把現(xiàn)在這個(gè)東西做完。”這是一種拒絕變化的開發(fā)方式。
軟件系統(tǒng)要保持與企業(yè)的目標(biāo)一致。時(shí)代在發(fā)展,人們的要求在不斷的提高,客戶的業(yè)務(wù)在不斷的發(fā)展。在這種情況下,傳統(tǒng)的先設(shè)計(jì)、再施工的V形式已經(jīng)不能適應(yīng)日益復(fù)雜的業(yè)務(wù)需要。軟件工程逐漸演化成下面這樣的過(guò)程:

說(shuō)明一下:
1、軟件開發(fā)的目標(biāo)要與企業(yè)目標(biāo)保持一致,一個(gè)開發(fā)周期不宜時(shí)間過(guò)長(zhǎng),一般控制在半年到一年。系統(tǒng)部署后,并不意味著開發(fā)工作結(jié)束了,而是進(jìn)入了下一個(gè)周期。
2、工程以循環(huán)迭代的方式前進(jìn),這并不意味輕視了設(shè)計(jì),不是要搞邊調(diào)研、邊設(shè)計(jì)、邊施工的“三邊”工程,相反,是更加重視設(shè)計(jì)的地位。軟件開發(fā)的全過(guò)程都需要設(shè)計(jì),軟件開發(fā)是“持續(xù)設(shè)計(jì)”的過(guò)程。同時(shí),設(shè)計(jì)工作也不只是簡(jiǎn)單過(guò)程分解、任務(wù)分配,而是概念設(shè)計(jì)、邏輯設(shè)計(jì)、物理設(shè)計(jì)等各個(gè)方面互相交織、齊頭并進(jìn)。
傳統(tǒng)的軟件開發(fā)方式使用一種非常理想化的流程——先與客戶討論項(xiàng)目的范圍,確定哪些需要做,哪些不需要做,然后規(guī)劃一個(gè)完美的設(shè)計(jì),不僅可以滿足現(xiàn)在的需求,還能很好的適應(yīng)未來(lái)的需求,設(shè)計(jì)完成后開始編碼,然后測(cè)試組裝,送到現(xiàn)場(chǎng)安裝調(diào)試運(yùn)行。這一系列過(guò)程就類似與發(fā)射一顆炮彈,首先要找到目標(biāo),然后根據(jù)地形、風(fēng)力、目標(biāo)的位置、移動(dòng)速度等各種因素,計(jì)算提前量、炮彈發(fā)射的角度,計(jì)算出一個(gè)拋物線軌道,最后在合適的時(shí)間把炮彈發(fā)射出去。這一切都符合最正確的物理定律,一切都聽起來(lái)很理想。如果沒(méi)有意外條件,當(dāng)然是可以擊中目標(biāo)的。但是炮彈一旦發(fā)射出去,一切就失去了控制,任何環(huán)境的變化都會(huì)造成偏離目標(biāo)。尤其是對(duì)于一個(gè)運(yùn)動(dòng)的目標(biāo)來(lái)說(shuō),計(jì)算過(guò)程十分復(fù)雜,很多情況下只能靠人估計(jì)。對(duì)于不規(guī)則的運(yùn)動(dòng)目標(biāo)只能碰碰運(yùn)氣。這樣的方式,命中率是很低的。
新的軟件開發(fā)過(guò)程不追求完美的、長(zhǎng)期的、理想的計(jì)劃,更加重視實(shí)際情況,重視需求的變化,提倡采用短期的計(jì)劃。這是一種擁抱變化的過(guò)程。就象是在炮彈上安裝了一個(gè)反饋裝置,鎖定目標(biāo)后,確保大方向的正確,然后就將炮彈發(fā)射出去。炮彈在運(yùn)行過(guò)程中不斷的將目標(biāo)位置偏移量輸入反饋電路,根據(jù)反饋輸出調(diào)整自己的運(yùn)行路線,無(wú)限的逼近目標(biāo)。這樣,炮彈就擁有了制導(dǎo)能力,命中率大大增加。
重構(gòu)就可以增加工程的調(diào)整能力,他可以把產(chǎn)品回復(fù)到一個(gè)穩(wěn)定的狀態(tài),可以基于這個(gè)狀態(tài)達(dá)到下一個(gè)目標(biāo)。如此反復(fù)前進(jìn),更好的滿足客戶的需求。
保持兼容性
重構(gòu)的目的在于改變系統(tǒng)的實(shí)現(xiàn)方式,而不改變?cè)械墓δ堋_@個(gè)過(guò)程中,判斷兼容性就十分的重要。一個(gè)子系統(tǒng)、模塊、類、函數(shù)是否與升級(jí)前保持兼容,如何判斷這個(gè)兼容性,如何保持這個(gè)兼容性,這關(guān)系到重構(gòu)的成本和重構(gòu)的可能性。
程序員學(xué)習(xí)寫程序代碼時(shí),會(huì)發(fā)現(xiàn)隨著程序代碼愈來(lái)愈多,許多的程序代碼不斷重復(fù)出現(xiàn)和被使用,因此很自然的開始使用例程、子程序或是過(guò)程、函數(shù)等機(jī)制幫助我們進(jìn)行程序代碼整理的工作。于是很自然的,字體的分析方式演化成這個(gè)樣子:將客戶的需求過(guò)程進(jìn)行分解,一步一步的分解,直到可以直接的實(shí)現(xiàn)他。這就是面向過(guò)程的分析方式。
面向過(guò)程的分析方式對(duì)變化的能力是很弱的。為什么呢?因?yàn)槊嫦蜻^(guò)程的分析方式很容易造成一種傾向——不區(qū)分行動(dòng)的主體。一個(gè)過(guò)程是沒(méi)有主體的,他不是在為自己工作,而是在為“別人”工作。當(dāng)我們修改了一個(gè)過(guò)程之后,我們很難判斷這個(gè)過(guò)程是否保持向后兼容,其他過(guò)程會(huì)不會(huì)受到影響。因?yàn)檫@個(gè)過(guò)程對(duì)外界有意義的不僅是他的輸入和輸出,還包括每一步過(guò)程,每一步過(guò)程都可能含有一個(gè)非常隱諱的業(yè)務(wù)意義,對(duì)外界產(chǎn)生影響。
因此,修改一個(gè)過(guò)程是非常困難的,通常升級(jí)一個(gè)面向過(guò)程的系統(tǒng),可以采用兩種方式:
1、寫新的過(guò)程;
2、在原有的過(guò)程上加開關(guān)參數(shù)。
除此以外的升級(jí)辦法都很難保證原過(guò)程和新過(guò)程的兼容性,容易造成錯(cuò)誤。
為了更好的保證升級(jí)后模塊的兼容性,應(yīng)該采用面向?qū)ο蟮姆治龇绞健0凑者@樣的分析方式,一個(gè)對(duì)象為“自己”工作,他有完整的、獨(dú)立的業(yè)務(wù)含義。對(duì)象之間通過(guò)接口發(fā)生聯(lián)系,一個(gè)對(duì)象對(duì)外界有影響的部分只有接口,至于他做什么、如何做、做的對(duì)不對(duì),則不是外界需要關(guān)心的事情。
判斷一個(gè)接口升級(jí)后是否保持兼容性就是一件比較容易的事情了。我們可以判斷接口的輸入輸出是否符合下面兩條規(guī)則:
1、升級(jí)后的輸入是升級(jí)前的輸入的超級(jí);
2、升級(jí)后的輸出是升級(jí)前的輸出的子集。
只要符合這兩點(diǎn),他就仍然可以在系統(tǒng)中運(yùn)行,不會(huì)對(duì)其他對(duì)象造成危害。在實(shí)際的工程中,判斷這個(gè)兼容性有一個(gè)更好的辦法:
自動(dòng)化的單元測(cè)試。
在重構(gòu)的過(guò)程中,自動(dòng)化的單元測(cè)試是非常好的保障。采用自動(dòng)化的單元測(cè)試,不斷運(yùn)行測(cè)試,可以保證系統(tǒng)的結(jié)構(gòu)改變的過(guò)程中,業(yè)務(wù)行為不發(fā)生改變。