張寶春
一、研究背景
軟件也是有生命周期的,在軟件使用過(guò)程中,軟件需要升級(jí)、更改,以適應(yīng)新的需求。一般軟件的更改都是迫于外部的壓力,譬如客戶要求或者是行業(yè)競(jìng)爭(zhēng)。而這些更改必然會(huì)對(duì)程序原有的結(jié)構(gòu)造成破壞,降低質(zhì)量,提高程序復(fù)雜度,結(jié)果軟件使用的維護(hù)成本被提高,以后再次升級(jí)將會(huì)變得困難。Brooks指出所有軟件都存在這個(gè)問(wèn)題,而且修復(fù)進(jìn)行得越晚,成本越高,代價(jià)越大,直到代價(jià)超過(guò)重新開(kāi)發(fā)。解決這個(gè)問(wèn)題,需要一種能夠提高軟件質(zhì)量同時(shí)降低程序復(fù)雜度的技術(shù)。重構(gòu)就是能夠解決這個(gè)問(wèn)題的一項(xiàng)重要技術(shù)手段。重構(gòu)是指在不改變軟件外部行為的前提下,以提高軟件內(nèi)部質(zhì)量為目的,對(duì)軟件實(shí)施更改的一種技術(shù)。重構(gòu)過(guò)程有三個(gè)階段:
1.識(shí)別:找出需要重構(gòu)的地方,確定重構(gòu)的方式;
2.代碼實(shí)施:對(duì)需要重構(gòu)的位置的代碼進(jìn)行重構(gòu);
3.驗(yàn)證:驗(yàn)證重構(gòu)后和重構(gòu)前的程序能否實(shí)現(xiàn)相同的功能,不能因?yàn)橹貥?gòu)改變代碼的功能。
我們重構(gòu)的目的是最終讓系統(tǒng)更加簡(jiǎn)單,也就是說(shuō),讓系統(tǒng)職責(zé)分配更清晰,代碼復(fù)雜度更低。
重構(gòu)的主要功能是通過(guò)軟件的修復(fù)來(lái)提高軟件的性能。因此,重構(gòu)可作為一項(xiàng)軟件輔助技術(shù)應(yīng)用到大部分軟件的維護(hù)活動(dòng)中去:
1.重構(gòu)是一種重要的預(yù)防性維護(hù)措施。它既不修正錯(cuò)誤,又不增加新功能,而是用于提高代碼可讀性或改變代碼內(nèi)部結(jié)構(gòu)與設(shè)計(jì)。它通過(guò)對(duì)類、變量和方法的重新分配,降低代碼冗余度,提高軟件質(zhì)量(如模塊性、重用性、復(fù)雜性、可維護(hù)性、可理解性和效率)。
2.重構(gòu)可用于逆向工程中,它能夠分析遺留系統(tǒng),使惡化的代碼重新組織,轉(zhuǎn)換成良好的形式。特別是我們需要給現(xiàn)有程序添加新的功能很困難的時(shí)候,開(kāi)發(fā)人員先進(jìn)行代碼重構(gòu),新功能的添加就會(huì)特別簡(jiǎn)單。
重構(gòu)的過(guò)程是對(duì)代碼進(jìn)行更改,這涉及裝程序,所以重構(gòu)不是一個(gè)簡(jiǎn)單的輕量級(jí)的軟件技術(shù),需要花費(fèi)大量的精力和時(shí)間:
1.識(shí)別程序中哪些地方存在“壞味道”、哪些代碼需要被重構(gòu)是重構(gòu)的難點(diǎn),因?yàn)閷?duì)于“壞味道”的理解取決于很多因素,在很大程度上存在主觀性。因此,如何自動(dòng)識(shí)別重構(gòu)對(duì)象是限制重構(gòu)技術(shù)發(fā)展的瓶頸,目前大部分工作都是基于人工的代碼檢測(cè),大大降低重構(gòu)效率,這也是制約重構(gòu)發(fā)展的最大障礙。
2.在重構(gòu)實(shí)施階段,需要對(duì)“壞味道”進(jìn)行調(diào)整,使得問(wèn)題代碼按照預(yù)期的方式進(jìn)行調(diào)整。對(duì)這些代碼的修改必定會(huì)影響到程序中的其他部分,需要為不同的“壞味道”設(shè)計(jì)安全的程序轉(zhuǎn)換。為了避免重構(gòu)過(guò)程牽涉到整個(gè)程序,要限定重構(gòu)的規(guī)模。在同一個(gè)程序中,同樣類型的“壞味道”會(huì)重復(fù)多次出現(xiàn),只是表現(xiàn)形式不同,要使一次重構(gòu)實(shí)施能同時(shí)修改所有具有相同類型的問(wèn)題代碼。這些都是在重構(gòu)實(shí)施階段需要被考慮的問(wèn)題。
3.重構(gòu)后的代碼必須能夠正確運(yùn)行,并具有與原程序相同的行為。不幸的是,對(duì)正確性和行為不變性的研究十分缺乏。這主要是因?yàn)槌绦虻男袨闀?huì)涉及運(yùn)行時(shí)的動(dòng)態(tài)信息,而重構(gòu)是一種靜態(tài)的代碼分析技術(shù)。要把動(dòng)態(tài)的行為通過(guò)靜態(tài)的方式來(lái)驗(yàn)證,是一項(xiàng)重大挑戰(zhàn)。
我們可以通過(guò)一種形式化的圖轉(zhuǎn)換技術(shù),建立一個(gè)完整的重構(gòu)模型。首先,把程序轉(zhuǎn)換成等價(jià)的圖表示,識(shí)別出圖中具有不合理關(guān)系的“壞味道”作為重構(gòu)對(duì)象;然后,通過(guò)程序切片技術(shù),提取與重構(gòu)對(duì)象相關(guān)的語(yǔ)句,按照?qǐng)D轉(zhuǎn)換規(guī)則對(duì)其實(shí)施重構(gòu);最后,驗(yàn)證被轉(zhuǎn)換的圖仍表示一個(gè)正確的程序,且轉(zhuǎn)換前后具有相同的行為。
二、重構(gòu)識(shí)別的現(xiàn)狀
目前,對(duì)重構(gòu)對(duì)象的識(shí)別主要依靠手動(dòng)方法,這樣不但效率低,而且正確性不高,從而制約了重構(gòu)技術(shù)的發(fā)展。因此,自動(dòng)識(shí)別重構(gòu)對(duì)象對(duì)重構(gòu)技術(shù)的發(fā)展非常重要。現(xiàn)有的重構(gòu)識(shí)別方法主要通過(guò)程序分析、可視化和度量等技術(shù)來(lái)輔助實(shí)現(xiàn)。
識(shí)別重構(gòu)對(duì)象的方法之一是靜態(tài)分析。Kamiy首先提出一種自動(dòng)識(shí)別克隆代碼的技術(shù),消除程序中的冗余代碼。Katao利用動(dòng)態(tài)分析技術(shù),開(kāi)發(fā)出能夠自動(dòng)檢測(cè)程序不變量的工具Daikon,進(jìn)而刪除不影響程序運(yùn)行的不變量。
另外一種識(shí)別重構(gòu)對(duì)象的方法是可視化技術(shù),它根據(jù)不同需求,將程序在不同層次進(jìn)行抽象,然后在這個(gè)抽象層次上發(fā)現(xiàn)潛在的“壞味道”。Simon利用基于度量的可視化技術(shù),把程序的內(nèi)聚度在2D圖上抽象出來(lái),輔助維護(hù)人員識(shí)別重構(gòu)對(duì)象。Bohnet將組件之間的交互(調(diào)用關(guān)系)可視化在3D空間,在不同的抽象層次(從方法間的交互到系統(tǒng)間的交互)上分析組件之間的交互,從而識(shí)別不合理的調(diào)用關(guān)系。
度量技術(shù)也是重構(gòu)識(shí)別的重要方法。如Chidamber提出的LCOM度量可反映一個(gè)類中的方法與屬性之間交互的緊密程度。Briand提出一種針對(duì)類的接口的度量,來(lái)分析這個(gè)類的接口定義的是否合理。Sheetz等通過(guò)Fan-out度量,來(lái)反映一個(gè)類與外界的耦合度,這可以幫助識(shí)別程序中類間交互的“壞味道”。Ott在一些簡(jiǎn)單度量的基礎(chǔ)之上,定義了一種基于切片的內(nèi)聚度度量,通過(guò)計(jì)算一個(gè)指定的變量在模塊內(nèi)的切片,可間接地計(jì)算該模塊的內(nèi)聚度,從而識(shí)別出一些重構(gòu)對(duì)象。而Simon也結(jié)合前人思想,定義出能夠精確反應(yīng)模塊內(nèi)聚度的度量函數(shù),根據(jù)元素間共享的屬性個(gè)數(shù)來(lái)決定模塊的內(nèi)聚度。這些方法都可用來(lái)輔助維護(hù)人員識(shí)別程序中某種“壞味道”。
三、重構(gòu)工具
在以上各種技術(shù)的支持下,學(xué)者們開(kāi)發(fā)出大量的重構(gòu)工具。XRefactory是一種帶有預(yù)處理功能的重構(gòu)工具,可以對(duì)C和Java程序進(jìn)行重構(gòu)。Smalltalk Refactoring Browser是一個(gè)半自動(dòng)化的重構(gòu)工具,維護(hù)人員通過(guò)選定一段帶有“壞味道”的代碼,并指定使用何種重構(gòu)方法,便可自動(dòng)執(zhí)行相應(yīng)的重構(gòu)。它已經(jīng)集成到許多IDES之中(如VisualWorks, TogetherJ, JBuilder, Eclipse)。另外,Casais提出對(duì)Eiffel程序進(jìn)行類層次關(guān)系重構(gòu)的工具。Guru是一種對(duì)Self程序進(jìn)行自動(dòng)化重構(gòu)的工具,可以從類層次結(jié)構(gòu)中提取出公共部分組成新的父類,也可以從方法中提取出共享代碼組成新的方法。同時(shí),Casais和Tichelaar分別提出降低程序?qū)嶓w間依賴性的重構(gòu)工具。Sunye把重構(gòu)與CASE工具進(jìn)行集成。Tourwe和Meuter指出被編譯器執(zhí)行的優(yōu)化也可以被看作是一種自動(dòng)化重構(gòu),雖然這些優(yōu)化轉(zhuǎn)換是對(duì)用戶完全透明的,但它的目的是提高程序的效力,并保持程序的外部行為不變。
在常用的重構(gòu)工具中,SlickEdit和Ref++是兩款功能完善的C++重構(gòu)工具,他們與Visaul Stuido平臺(tái)直接集成,可以對(duì)代碼實(shí)施Extract Method和Modify Parameter等十多種重構(gòu)。而XRefactory]和JFactor是被廣泛使用的Java自動(dòng)化重構(gòu)工具,被無(wú)縫集成到Eclipse平臺(tái)中,實(shí)施一些簡(jiǎn)單的原子重構(gòu),輔助維護(hù)人員對(duì)程序的結(jié)構(gòu)進(jìn)行改善。在某些情況下,全自動(dòng)化工具存在一些劣勢(shì),因?yàn)橹貥?gòu)全部自動(dòng)化,可能做了太多的工作,會(huì)使一部分被重構(gòu)后的程序比之前更難理解。交互式重構(gòu)工具可能不存在這樣的劣勢(shì),但交互式重構(gòu)工具需要大量的人機(jī)交互,這是一件耗時(shí)的事情。不論怎樣,半自動(dòng)化重構(gòu)工具在實(shí)際應(yīng)用中的使用范圍最廣,因?yàn)橐恍┲貥?gòu)所需的知識(shí)不能從軟件中被提取,卻可以從人腦中得知。
四、研究?jī)?nèi)容
目前對(duì)重構(gòu)技術(shù)的研究比較分散,對(duì)于重構(gòu)的不同階段,使用的技術(shù)各不相同,缺少統(tǒng)一的重構(gòu)模型和基礎(chǔ)技術(shù)支持。我們可以用基于程序切片的形式化方法對(duì)程序進(jìn)行重構(gòu)識(shí)別、實(shí)施和驗(yàn)證。這種形式化的重構(gòu)模型以圖表示作為基礎(chǔ)技術(shù),把一個(gè)程序用圖表示出來(lái),那么重構(gòu)就相當(dāng)于圖轉(zhuǎn)換文法中的一條產(chǎn)生式規(guī)則,一個(gè)重構(gòu)的前置條件對(duì)應(yīng)為圖轉(zhuǎn)換文法中一條產(chǎn)生式的前置條件。該方法先把程序轉(zhuǎn)換成等價(jià)的圖表示,所有的重構(gòu)過(guò)程都在圖的基礎(chǔ)上完成,形成一個(gè)統(tǒng)一的重構(gòu)體系。
最主要的研究?jī)?nèi)容為以下三方面:
1.重構(gòu)識(shí)別
在圖中構(gòu)造元素之間的依賴關(guān)系,生成程序依賴圖,在此基礎(chǔ)上定義一種度量函數(shù)來(lái)計(jì)算元素間的控制關(guān)系,根據(jù)度量結(jié)果,識(shí)別元素間不合理的耦合關(guān)系,從而自動(dòng)識(shí)別出程序中的“壞味道”。該技術(shù)可以自動(dòng)分析源代碼,指出程序中不合理的地方,改變了以往手動(dòng)重構(gòu)識(shí)別所帶來(lái)的主觀性和效率問(wèn)題,解決了重構(gòu)的瓶頸,為自動(dòng)化重構(gòu)實(shí)施打下良好基礎(chǔ)。
2.重構(gòu)實(shí)施
通過(guò)向圖中的節(jié)點(diǎn)和邊中加入類型信息,把程序依賴圖擴(kuò)展為類型圖。類型圖包含了重構(gòu)實(shí)施過(guò)程所需的必要信息。在重構(gòu)和類型圖轉(zhuǎn)換之間建立起等價(jià)關(guān)系,將程序的重構(gòu)過(guò)程變?yōu)閷?duì)圖的轉(zhuǎn)換過(guò)程。通過(guò)類型圖的轉(zhuǎn)換產(chǎn)生式表示重構(gòu),可以使重構(gòu)對(duì)象按照預(yù)想的方式進(jìn)行調(diào)整,并使一次重構(gòu)實(shí)施能夠同時(shí)修改所有具有相同類型的問(wèn)題代碼。為了避免重構(gòu)過(guò)程牽涉到整個(gè)程序,減少重構(gòu)的影響集,可以借助程序切片技術(shù)計(jì)算出與重構(gòu)對(duì)象相關(guān)的代碼,使重構(gòu)過(guò)程僅在切片中進(jìn)行。
3.重構(gòu)驗(yàn)證
為保證重構(gòu)的正確性,可以預(yù)先為重構(gòu)產(chǎn)生式定義一些前置條件,使得能夠滿足條件的程序才能被重構(gòu),且保持程序的正確性。在保證重構(gòu)正確性的前提下,才能驗(yàn)證重構(gòu)是否保持程序行為的不變性。本文基于近似性原理,定義一些能夠代表程序行為的圖屬性,通過(guò)驗(yàn)證在重構(gòu)實(shí)施前后圖的行為屬性是否被改變,從而證明重構(gòu)是否保證程序的行為不變性。該方法避免了使用靜態(tài)方法驗(yàn)證程序的動(dòng)態(tài)行為帶來(lái)的弊端。
五、總結(jié)展望
通過(guò)識(shí)別出軟件中的“壞味道”,對(duì)其實(shí)施重構(gòu),調(diào)整軟件的結(jié)構(gòu)使之更加合理,從而提高軟件質(zhì)量,進(jìn)而有利于程序理解和錯(cuò)誤定位等其他軟件維護(hù)活動(dòng),大大降低軟件維護(hù)的成本。隨著統(tǒng)一重構(gòu)模型技術(shù)的發(fā)展逐漸成熟,它勢(shì)必會(huì)得到軟件維護(hù)人員的重用,成為必不可少的一項(xiàng)軟件維護(hù)手段。
【參考文獻(xiàn)】
[1]Brooks F. The Mythical Man-Month [M]. New York: Addison-Wesley, 1975.
[2] Emden E V, Moonen L. Java quality assurance by detecting code smells [C] . Proceedings of Working Conference Reverse Engineering, Amsterdam, Netherlands, 2002: 97-108.
[3]Czibula I G, erban G. Improving Systems Design Using a Clustering Approach [J]. International Journal of Computer Science and Network Security, 2006, 6(12):40-48.
[4]Sunye G, Pollet D, LeTraon Y, et al. Refactoring UML models [J]. Lecture Notes in Computer Science, 2001, 21(85): 134-138.