張寶春
一、研究背景
軟件也是有生命周期的,在軟件使用過程中,軟件需要升級、更改,以適應新的需求。一般軟件的更改都是迫于外部的壓力,譬如客戶要求或者是行業競爭。而這些更改必然會對程序原有的結構造成破壞,降低質量,提高程序復雜度,結果軟件使用的維護成本被提高,以后再次升級將會變得困難。Brooks指出所有軟件都存在這個問題,而且修復進行得越晚,成本越高,代價越大,直到代價超過重新開發。解決這個問題,需要一種能夠提高軟件質量同時降低程序復雜度的技術。重構就是能夠解決這個問題的一項重要技術手段。重構是指在不改變軟件外部行為的前提下,以提高軟件內部質量為目的,對軟件實施更改的一種技術。重構過程有三個階段:
1.識別:找出需要重構的地方,確定重構的方式;
2.代碼實施:對需要重構的位置的代碼進行重構;
3.驗證:驗證重構后和重構前的程序能否實現相同的功能,不能因為重構改變代碼的功能。
我們重構的目的是最終讓系統更加簡單,也就是說,讓系統職責分配更清晰,代碼復雜度更低。
重構的主要功能是通過軟件的修復來提高軟件的性能。因此,重構可作為一項軟件輔助技術應用到大部分軟件的維護活動中去:
1.重構是一種重要的預防性維護措施。它既不修正錯誤,又不增加新功能,而是用于提高代碼可讀性或改變代碼內部結構與設計。它通過對類、變量和方法的重新分配,降低代碼冗余度,提高軟件質量(如模塊性、重用性、復雜性、可維護性、可理解性和效率)。
2.重構可用于逆向工程中,它能夠分析遺留系統,使惡化的代碼重新組織,轉換成良好的形式。特別是我們需要給現有程序添加新的功能很困難的時候,開發人員先進行代碼重構,新功能的添加就會特別簡單。
重構的過程是對代碼進行更改,這涉及裝程序,所以重構不是一個簡單的輕量級的軟件技術,需要花費大量的精力和時間:
1.識別程序中哪些地方存在“壞味道”、哪些代碼需要被重構是重構的難點,因為對于“壞味道”的理解取決于很多因素,在很大程度上存在主觀性。因此,如何自動識別重構對象是限制重構技術發展的瓶頸,目前大部分工作都是基于人工的代碼檢測,大大降低重構效率,這也是制約重構發展的最大障礙。
2.在重構實施階段,需要對“壞味道”進行調整,使得問題代碼按照預期的方式進行調整。對這些代碼的修改必定會影響到程序中的其他部分,需要為不同的“壞味道”設計安全的程序轉換。為了避免重構過程牽涉到整個程序,要限定重構的規模。在同一個程序中,同樣類型的“壞味道”會重復多次出現,只是表現形式不同,要使一次重構實施能同時修改所有具有相同類型的問題代碼。這些都是在重構實施階段需要被考慮的問題。
3.重構后的代碼必須能夠正確運行,并具有與原程序相同的行為。不幸的是,對正確性和行為不變性的研究十分缺乏。這主要是因為程序的行為會涉及運行時的動態信息,而重構是一種靜態的代碼分析技術。要把動態的行為通過靜態的方式來驗證,是一項重大挑戰。
我們可以通過一種形式化的圖轉換技術,建立一個完整的重構模型。首先,把程序轉換成等價的圖表示,識別出圖中具有不合理關系的“壞味道”作為重構對象;然后,通過程序切片技術,提取與重構對象相關的語句,按照圖轉換規則對其實施重構;最后,驗證被轉換的圖仍表示一個正確的程序,且轉換前后具有相同的行為。
二、重構識別的現狀
目前,對重構對象的識別主要依靠手動方法,這樣不但效率低,而且正確性不高,從而制約了重構技術的發展。因此,自動識別重構對象對重構技術的發展非常重要。現有的重構識別方法主要通過程序分析、可視化和度量等技術來輔助實現。
識別重構對象的方法之一是靜態分析。Kamiy首先提出一種自動識別克隆代碼的技術,消除程序中的冗余代碼。Katao利用動態分析技術,開發出能夠自動檢測程序不變量的工具Daikon,進而刪除不影響程序運行的不變量。
另外一種識別重構對象的方法是可視化技術,它根據不同需求,將程序在不同層次進行抽象,然后在這個抽象層次上發現潛在的“壞味道”。Simon利用基于度量的可視化技術,把程序的內聚度在2D圖上抽象出來,輔助維護人員識別重構對象。Bohnet將組件之間的交互(調用關系)可視化在3D空間,在不同的抽象層次(從方法間的交互到系統間的交互)上分析組件之間的交互,從而識別不合理的調用關系。
度量技術也是重構識別的重要方法。如Chidamber提出的LCOM度量可反映一個類中的方法與屬性之間交互的緊密程度。Briand提出一種針對類的接口的度量,來分析這個類的接口定義的是否合理。Sheetz等通過Fan-out度量,來反映一個類與外界的耦合度,這可以幫助識別程序中類間交互的“壞味道”。Ott在一些簡單度量的基礎之上,定義了一種基于切片的內聚度度量,通過計算一個指定的變量在模塊內的切片,可間接地計算該模塊的內聚度,從而識別出一些重構對象。而Simon也結合前人思想,定義出能夠精確反應模塊內聚度的度量函數,根據元素間共享的屬性個數來決定模塊的內聚度。這些方法都可用來輔助維護人員識別程序中某種“壞味道”。
三、重構工具
在以上各種技術的支持下,學者們開發出大量的重構工具。XRefactory是一種帶有預處理功能的重構工具,可以對C和Java程序進行重構。Smalltalk Refactoring Browser是一個半自動化的重構工具,維護人員通過選定一段帶有“壞味道”的代碼,并指定使用何種重構方法,便可自動執行相應的重構。它已經集成到許多IDES之中(如VisualWorks, TogetherJ, JBuilder, Eclipse)。另外,Casais提出對Eiffel程序進行類層次關系重構的工具。Guru是一種對Self程序進行自動化重構的工具,可以從類層次結構中提取出公共部分組成新的父類,也可以從方法中提取出共享代碼組成新的方法。同時,Casais和Tichelaar分別提出降低程序實體間依賴性的重構工具。Sunye把重構與CASE工具進行集成。Tourwe和Meuter指出被編譯器執行的優化也可以被看作是一種自動化重構,雖然這些優化轉換是對用戶完全透明的,但它的目的是提高程序的效力,并保持程序的外部行為不變。
在常用的重構工具中,SlickEdit和Ref++是兩款功能完善的C++重構工具,他們與Visaul Stuido平臺直接集成,可以對代碼實施Extract Method和Modify Parameter等十多種重構。而XRefactory]和JFactor是被廣泛使用的Java自動化重構工具,被無縫集成到Eclipse平臺中,實施一些簡單的原子重構,輔助維護人員對程序的結構進行改善。在某些情況下,全自動化工具存在一些劣勢,因為重構全部自動化,可能做了太多的工作,會使一部分被重構后的程序比之前更難理解。交互式重構工具可能不存在這樣的劣勢,但交互式重構工具需要大量的人機交互,這是一件耗時的事情。不論怎樣,半自動化重構工具在實際應用中的使用范圍最廣,因為一些重構所需的知識不能從軟件中被提取,卻可以從人腦中得知。
四、研究內容
目前對重構技術的研究比較分散,對于重構的不同階段,使用的技術各不相同,缺少統一的重構模型和基礎技術支持。我們可以用基于程序切片的形式化方法對程序進行重構識別、實施和驗證。這種形式化的重構模型以圖表示作為基礎技術,把一個程序用圖表示出來,那么重構就相當于圖轉換文法中的一條產生式規則,一個重構的前置條件對應為圖轉換文法中一條產生式的前置條件。該方法先把程序轉換成等價的圖表示,所有的重構過程都在圖的基礎上完成,形成一個統一的重構體系。
最主要的研究內容為以下三方面:
1.重構識別
在圖中構造元素之間的依賴關系,生成程序依賴圖,在此基礎上定義一種度量函數來計算元素間的控制關系,根據度量結果,識別元素間不合理的耦合關系,從而自動識別出程序中的“壞味道”。該技術可以自動分析源代碼,指出程序中不合理的地方,改變了以往手動重構識別所帶來的主觀性和效率問題,解決了重構的瓶頸,為自動化重構實施打下良好基礎。
2.重構實施
通過向圖中的節點和邊中加入類型信息,把程序依賴圖擴展為類型圖。類型圖包含了重構實施過程所需的必要信息。在重構和類型圖轉換之間建立起等價關系,將程序的重構過程變為對圖的轉換過程。通過類型圖的轉換產生式表示重構,可以使重構對象按照預想的方式進行調整,并使一次重構實施能夠同時修改所有具有相同類型的問題代碼。為了避免重構過程牽涉到整個程序,減少重構的影響集,可以借助程序切片技術計算出與重構對象相關的代碼,使重構過程僅在切片中進行。
3.重構驗證
為保證重構的正確性,可以預先為重構產生式定義一些前置條件,使得能夠滿足條件的程序才能被重構,且保持程序的正確性。在保證重構正確性的前提下,才能驗證重構是否保持程序行為的不變性。本文基于近似性原理,定義一些能夠代表程序行為的圖屬性,通過驗證在重構實施前后圖的行為屬性是否被改變,從而證明重構是否保證程序的行為不變性。該方法避免了使用靜態方法驗證程序的動態行為帶來的弊端。
五、總結展望
通過識別出軟件中的“壞味道”,對其實施重構,調整軟件的結構使之更加合理,從而提高軟件質量,進而有利于程序理解和錯誤定位等其他軟件維護活動,大大降低軟件維護的成本。隨著統一重構模型技術的發展逐漸成熟,它勢必會得到軟件維護人員的重用,成為必不可少的一項軟件維護手段。
【參考文獻】
[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.