Java 項目開發(fā)過程中,由于開發(fā)人員的經(jīng)驗、代碼風(fēng)格各不相同,以及缺乏統(tǒng)一的標準和管理流程,往往導(dǎo)致整個項目的代碼質(zhì)量較差,難于維護,需要較大的測試投入 和周期等問題。這些問題在一個項目組初建、需求和設(shè)計均具有不完全可預(yù)期性和完備性的全新項目中將尤為突出。本文將結(jié)合敏捷開發(fā)周期短,變化快等特點,介 紹如何通過在開發(fā)過程中采取一系列步驟來保證和提高整個開發(fā)團隊的代碼質(zhì)量,并闡述了每一步可以利用的工具和最佳實踐,從而使開發(fā)過程更加規(guī)范化,成就高 質(zhì)量的代碼,減少測試的投入,并促進整個團隊的技能提高,最終提高開發(fā)效率和質(zhì)量。
如圖 1 所示,敏捷開發(fā)過程經(jīng)歷需求調(diào)研,用例分析和用例分解,進入開發(fā)迭代階段。在每個迭代過程中,可以采用以下五個步驟來保證和提高整個項目的代碼質(zhì)量:統(tǒng)一 編碼規(guī)范、代碼樣式;靜態(tài)代碼分析(static code review);單元測試;持續(xù)集成;代碼評審和重構(gòu)(Review & Refactor)。下文將針對每個步驟和其所使用的工具、方法進行詳細描述。
圖 1. 敏捷開發(fā)中的 Java 代碼質(zhì)量保證步驟
規(guī)范統(tǒng)一的編碼會增加項目代碼的可讀性和可維護性,但實際情況往往是項目組內(nèi)的 Java 代碼開發(fā)人員的編碼風(fēng)格常常各不相同,這可能是由于不同的經(jīng)驗習(xí)慣或者缺乏編碼規(guī)范方面的學(xué)習(xí)造成的。這樣一來,其他項目成員或者維護人員在閱讀項目代碼 時就需要花費更多的時間來理解代碼作者的意圖,所以制定并采取統(tǒng)一的編碼規(guī)范就顯得很重要。編碼規(guī)范主要應(yīng)包含以下幾個方面:
項目的編碼規(guī)范可以參考已有的一些 Java 編程規(guī)范書籍和其他相關(guān)資料并結(jié)合項目的本身來制定,可供參考的書籍有《 Java 編程風(fēng)格》(英文書名為:The Elements of Java Style)。編碼規(guī)范要形成文檔,而且要簡潔明了,并組織項目成員一起學(xué)習(xí),確保所有成員正確理解所有條目。
一旦編碼規(guī)范確定,就可以利用 Eclipse 自身提供的功能來控制代碼樣式和格式。具體做法是,點擊 Eclipse 的Windows -> Preference 菜單項,在打開的 Preferences 對話框的左側(cè)欄中找到 Java 節(jié)點下的子項 Code Style(如圖 2),該項和它的子項允許您對 Java 代碼的樣式進行控制。
例如,為了使用自動格式化工具,可以在 Eclipse 提供的默認代碼格式配置的基礎(chǔ)上建立自定義的格式。在Formatter 面板中,點擊 New,輸入新的名字并選擇一個默認的配置作為初始化格式,如圖 3 所示。
單擊 OK 后就可以在新打開的窗口中進行修改定制自己需要的格式。如圖 4 所示。
修改完成后點擊 Apply 保存所作修改。同時可以點擊 Export 將當前的格式定義導(dǎo)出成一個 XML 文件,這樣項目組的其他成員就可以很方便通過點擊圖 3 中的 Import 按鈕來導(dǎo)入該 XML 文件來使用同一個代碼格式定義。
這樣每次在提交代碼到版本控制服務(wù)器前就可以通過 Eclipse 界面里的 Source->Format 菜單來對代碼進行格式化,從而使整個項目的代碼具有相同的格式。同樣可以通過對 Code Style 下的其他項目進行設(shè)置來幫助對 Java 代碼的樣式進行控制。將所有這些樣式文件導(dǎo)出成 XML 文件后,同編碼規(guī)范一起歸檔,供所有項目成員使用。
在完成源代碼的開發(fā)以后,下面要進行的工作就是審視和測試代碼。除了通過運行測試代碼來檢查功能之外,還能利用一些靜態(tài)分析工具來快速、直接 地提高代碼質(zhì)量。靜態(tài)代碼分析工具并不需要運行代碼,可以直接對 Java 文件和 Class 文件進行分析,通過一些檢查條件的設(shè)置,快速找到代碼中的錯誤和潛在缺陷?,F(xiàn)在的靜態(tài)分析工具很多,有 FindBugs、PMD、IBM Rational Tool,等等。在這里,選擇 FindBugs 作為靜態(tài)代碼分析工具。FindBugs 可以和日常開發(fā)工具 Eclipse 進行集成,在開發(fā)過程中,就可以方便的開始靜態(tài)代碼的檢查。通過檢查 Class 文件或者 JAR 文件,將字節(jié)碼和一組缺陷模式進行對比,來發(fā)現(xiàn)可能存在的代碼問題。在 Eclipse 的開發(fā)環(huán)境中,用插件安裝的方式安裝了 Findbugs 后,在 Eclipse 的配置選項中就會多出來 FindBugs 的配置選項??梢詫ψ约旱捻椖窟M行配置,選擇需要的 Detector 檢查代碼。
設(shè)置好自己的規(guī)則后,在需要檢查的代碼文件夾上點擊右鍵,就可以啟動 FindBugs 檢查。代碼可以是一個項目,也可以只是幾個文件。
檢查完畢后,會出現(xiàn) FindBugs 視圖,把所有檢查的結(jié)果根據(jù)錯誤分組展示。點擊結(jié)果里面的每一個錯誤,會自動打開對應(yīng)的代碼。當根據(jù)規(guī)則改正了所有的錯誤,或者說潛在錯誤,這些代碼也就 通過了靜態(tài)代碼檢查。FindBugs 的檢查結(jié)果可以是 XML 文件,也可以是文本文件,便于項目的集成管理和檢查保存。
單元測試是軟件開發(fā)過程中重要的質(zhì)量保證環(huán)節(jié),在此環(huán)節(jié)中,設(shè)計和評審對于保證整個單元測試過程的完整性和有效性來說十分重要。設(shè)計階段需要 具體考慮要對哪些代碼單元進行測試,被測單元之間的關(guān)系,測試策略,以及單元測試用例設(shè)計等,并最終輸出《單元測試用例設(shè)計》文檔,用來指導(dǎo)具體的單元測 試執(zhí)行。在用例設(shè)計中,通過對代碼單元輸入和期待輸出的定義來保證該單元的功能正確性,邊界值的測試和異常測試非常重要。同時也配合測試用例和功能塊的匹 配方法來衡量用例設(shè)計的完整性。
在用例設(shè)計完成之后,下一步的工作就是進行測試用例的評審。個人的理解和經(jīng)驗始終是有限的,用例評審可以借集體之力,對用例設(shè)計進入查漏補 缺,進一步保證測試用例的有效性。由于單元測試屬于白盒測試范疇,它主要通過對代碼的邏輯結(jié)構(gòu)進行分析來設(shè)計測試用例,因此,評審員的選擇最好以理解代碼 邏輯結(jié)構(gòu)為前提,如果評審員來自相關(guān)模塊,還能夠有效的發(fā)現(xiàn)模塊相關(guān)性和依賴性所帶來的問題。
在實際項目中,開發(fā)人員自己的代碼往往需要和其他的代碼模塊或系統(tǒng)進行交互,但在測試的過程中,這些需要被調(diào)用的真實對象常常很難被實例化, 或者這些對象在某些情況下無法被用來測試,例如,真實對象的行為無法預(yù)測,真實對象的行為難以觸發(fā),或者真實對象的運行速度很慢。這時候,就需要使用模擬 對象技術(shù)(Mock),利用一個模擬對象來模擬我們的代碼所依賴的真實對象,來幫助完成測試,提高測試覆蓋率,從而提高代碼質(zhì)量。模擬對象技術(shù)利用了在面 向接口的編程中,由于代碼直接對接口進行調(diào)用,所以代碼并不知道引用的是真實對象還是模擬對象,這樣就可以順利的完成對代碼的測試。
模擬技術(shù)有很多種,如 jMock,EasyMock,Mockito,PowerMock 等等。其中 Mockito 消除了對期望行為的需求,避免了這些代碼的大量初始化。
在模擬對象過程中,先模擬一個需要調(diào)用的 List 對象 LinkedList,再設(shè)定這個對象的行為,當調(diào)用 get(0) 的時候,返回”first”。這樣,測試代碼就可以利用這個對象來測試我們的功能代碼,需要調(diào)用和返回值的時候,可以順利的得到模擬對象的返回值。也需要 對模擬對象進行錯誤情況的模擬,保證代碼對錯誤的處理的正確性。
為了衡量單元測試的質(zhì)量和覆蓋的范圍,需要對單元測試的代碼進行測試覆蓋分析。常用的衡量測試覆蓋率的指標主要有語句覆蓋率、分支覆蓋率、路 徑覆蓋率、條件覆蓋率和方法覆蓋率等。具體采用哪些指標可以根據(jù)項目的實際情況來定,以避免因過高的指標增加了代碼開發(fā)人員的工作量而影響了項目整體的進 度。
EMMA 是一款比較流行的開源 Java 測試覆蓋率分析工具,支持類、方法、代碼行、基本代碼塊等多種類型的測試覆蓋率分析,支持將覆蓋率分析結(jié)果導(dǎo)出為多種格式的報告,并采用多種顏色來高亮顯 示不同的覆蓋率狀態(tài)。EclEmma 是一款基于 EMMA 的 Eclipse 插件,方便在 Eclipse IDE 中進行測試覆蓋率分析。如圖 9,在測試用例寫好后,可以在右鍵點擊測試類,選擇 Coverage As -> JUnit Test.
單元測試跑完后,Coverage視圖中會顯示所選擇的測試的覆蓋率。雙擊打開某一具體的類后,可以看到 高亮顯示的覆蓋分析結(jié)果,如圖 10 所示。紅色代表測試沒有覆蓋到該行,黃色表示部分覆蓋,綠色的行表示該行在本次測試中被覆蓋到。
在 Coverage 視圖中可以通過點擊鼠標右鍵將測試覆蓋分析的結(jié)果導(dǎo)出成需要的格式,例如 HTML。
圖 12 顯示了導(dǎo)出的 report。
為了保證單元測試的有效性和質(zhì)量,可以規(guī)定一個測試覆蓋率的下限,例如所有的包和類的覆蓋率必須達到 80% 以上。不過值得注意的是,不要單純追求高覆蓋率,要同時注意測試用例的質(zhì)量,如果測試用例本身就寫的有錯誤,那么即使測試覆蓋率很高也沒有意義。
持續(xù)集成(Continuous Integration)是利用一系列的工具,方法和規(guī)則,做到快速的構(gòu)建開發(fā)代碼,自動的測試化,來提高開發(fā)代碼的效率和質(zhì)量。利用自動構(gòu)建工具,隨時 都能把提交的代碼構(gòu)建出來,提供一個可以測試使用的版本,讓用戶和開發(fā)人員同時看到相同的功能,盡早的發(fā)現(xiàn)問題和錯誤,也可以盡快的得到測試人員和用戶的 反饋。
要做到持續(xù)集成,就要利用一系列工具,把開發(fā)過程中的重復(fù)工作自動化。搭建自動的構(gòu)建服務(wù)器,自動的進行單元測試和發(fā)布新版本,一個集成的服 務(wù)器可以提供構(gòu)建過程的結(jié)果報告,自動通知開發(fā)人員構(gòu)建結(jié)果,并且保存歷史數(shù)據(jù)。IBM Rational Team Concert (RTC) 可以提供工作任務(wù)的管理,項目計劃的安排,代碼版本管理控制,自動構(gòu)建可用版本,生成構(gòu)建結(jié)果報告。這些過程構(gòu)成了項目的持續(xù)集成過程,其中,版本的自動 構(gòu)建和代碼的自動單元測試是持續(xù)集成的關(guān)鍵過程,RTC 在這些過程上提供了有力的支持。
RTC 提供了 build engine 來負責(zé)構(gòu)建 build,首選,啟動 build engine,并和 RTC 服務(wù)器建立了連接。再創(chuàng)建項目的 build 定義。在這個定義中,需要設(shè)定編譯哪些模塊的代碼,需要跳動哪個 ANT 文件來啟動編譯,和一些編譯過程中的參數(shù)的設(shè)定。當這些都準備好了,編譯對于項目而言,就變成一個簡單的事情。
可以看到,通過在 build 定義上,點擊請求構(gòu)建,就可以觸發(fā)一次構(gòu)建過程。選擇需要的構(gòu)建參數(shù),這個過程就會在后臺運行。每一個開發(fā)人員,做了稍許的代碼改變和提交,都可以觸發(fā)新 的構(gòu)建過程,來保證我們代碼的有效性。申請一個新的構(gòu)建的過程如圖 13、圖 14 所示。
當構(gòu)建結(jié)束后。RTC 服務(wù)器會提供構(gòu)建結(jié)果報告。開發(fā)人員可以查詢到這次構(gòu)建的詳細信息。
整個開發(fā)過程中,構(gòu)建版本的過程應(yīng)該是無數(shù)次的,通過每次構(gòu)建,都可以得到當時代碼的編譯情況,并且可以得到一個可運行的軟件版本。在構(gòu)建定 義上,RTC 支持設(shè)置構(gòu)建計劃。定時自動的觸發(fā)一次構(gòu)建。
構(gòu)建可以自動了,重點提高代碼質(zhì)量的單元測試呢?如果每一天的代碼,每一個版本的代碼,都已經(jīng)通過了我們的單元測試,這樣我們就能對代碼的質(zhì) 量有了基本的保證。在構(gòu)建腳本的自動調(diào)用過程中,通過 ANT 的腳本,可以加上 JUnit,EMMA,F(xiàn)indBugs 的 ANT 腳本調(diào)用,每一次的構(gòu)建,都可以把這些檢查工作自動的進行一遍測試。這些測試都要生成測試結(jié)果報告, RTC 不能提供這些報告的展示,就可以利用 Hudson 這個開源工具,集成測試報告來方便查閱。
代碼評審(Code Review)是 Java 項目開發(fā)過程中的一個重要步驟,代碼評審可以幫助發(fā)現(xiàn)靜態(tài)代碼分析過程中無法發(fā)現(xiàn)的一些問題,例如代碼的編寫是否符合編碼規(guī)范,代碼在邏輯上或者功能上是 否存在錯誤,代碼在執(zhí)行效率和性能上是否有需要改進的地方,代碼的注釋是否完整正確,代碼是否存在冗余和重復(fù)。代碼評審還可以幫助新進入項目組的成員快速 學(xué)習(xí)和了解項目,促進經(jīng)驗分享,同時也能保證項目成員的良好溝通。代碼評審主要包括兩種形式,同級評審(Peer Review)和小組評審(Group Review)。同級評審主要指項目成員間的互相評審,小組評審是指通過召開評審會議,項目成員一起對項目代碼進行評審。
為了提高代碼評審的有效性和效率,可以借助一些外部工具,比較常用的代碼評審工具有 Jupiter 和 Code Striker。Jupiter 是一款開源的 Eclipse 插件,允許成員將評審意見定位到真實代碼的具體行,由于代碼評審的結(jié)果以 XML 文件的形式保存,所以可以把結(jié)果提交到版本管理服務(wù)器進行共享。圖 18 顯示了使用 Jupiter 進行代碼評審的界面。
在代碼評審任務(wù)創(chuàng)建后,Jupiter 將代碼評審分成三個階段,個人評審階段 (Individual Phase)、團隊評審階段(Team Phase)和問題修復(fù)階段(Rework Phase)。在個人評審階段,評審成員將發(fā)現(xiàn)的代碼問題或者缺陷記錄下來,每個問題都會作為一個記錄保存在評審表格中。在團隊評審階段,團隊的全部或者 部分成員會一起對個人評審階段發(fā)現(xiàn)的問題進行定性,如果問題確實存在,就將該問題分配給某個成員去解決,并在 Jupiter 中將該問題設(shè)置成相應(yīng)的狀態(tài)。在問題修復(fù)階段,團隊成員會修復(fù)屬于自己的問題,并將相應(yīng)的記錄設(shè)置成已解決等正確的狀態(tài)。
Codestriker 是一款基于 Web 的常用代碼評審工具,對代碼的評審可以針對某一具體行,也可以針對整個代碼文件,評審意見會被保存在數(shù)據(jù)庫中。評審人員可以同時看到其他人的評論,代碼作 者也可以針對某一具體的評論回復(fù)。Codestriker 支持郵件通知,還可以同版本控制服務(wù)器進行集成,以跟蹤和顯示文件內(nèi)容的改變。圖 19 顯示了 Codestriker 的界面。
在實踐中對所有代碼進行小組評審會比較費時,所以可以根據(jù)實際情況來挑選一些核心代碼進行小組評審,或者在項目的前期安排較多的小組評審,等 項目組的成員對代碼評審的標準和要求有較好的理解,進行代碼評審的經(jīng)驗提高后,就可以逐漸減少小組評審的次數(shù),從而達到大部分代碼即使只進行同級評審也能 保證很好的質(zhì)量。
通過代碼評審發(fā)現(xiàn)的問題要通過代碼重構(gòu)及時解決掉,較小的不涉及多人代碼的重構(gòu)可以由項目成員自己借助 Eclipse 的重構(gòu)功能完成,不同項目成員寫的實現(xiàn)相同功能的不同代碼要通過討論整合成公共的類或者方法。比較復(fù)雜的或者比較高層次的重構(gòu)工作,例如整個項目層面的代 碼組織形式的改變需要由整個項目組共同討論完成。
軟件開發(fā)沒有一成不變、萬能通用的流程和方法,希望大家能從本文得到啟發(fā)和收益,結(jié)合您的實際項目特點,實踐以上步驟和方法,并加以完善和改 進,共同打造高效高質(zhì)量的 Java 代碼,為您的項目成功奠定堅實的基礎(chǔ)。