# 讓開發自動化: 持續檢查
_把自己從軟件檢查員尋常的手工檢查工作中解放出來_
在開始新項目時,多數人計劃在將代碼投入生產發行之前審核它們;但是,當提交日程超越了其他因素時,審核常常成為第一個被拋棄的實踐。如果能夠自動執行其中一些審核,那么情況又會怎樣呢?在新系列 “_[讓開發自動化](http://www.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&search_by=%E8%AE%A9%E5%BC%80%E5%8F%91%E8%87%AA%E5%8A%A8%E5%8C%96)_” 的第一篇文章中,開發自動化專家 Paul Duvall 首先將研究如何自動化檢查器(例如 CheckStyle、JavaNCSS 和 CPD)、如何改進開發過程,以及應該什么時候使用它們。
代碼檢查可以采用不同的形式。有些企業使用正式的同級評審(peer review),在該評審過程中,開發人員要為代碼提供同級評價,并提供改進意見;其他一些企業使用結對編程;還有一些人則考慮使用高級設計決策和推薦的代碼改進。有些團隊會在將代碼提交到版本控制存儲庫之前,讓其他開發人員用 “桌面檢查” 的形式審查他們的代碼。
不論企業采用哪種方式進行代碼檢查,有一件事是肯定的:_它們幾乎都是手工過程_。正如我多年所觀察到的,手工過程很容易出錯,如果工作緊張,就會忘記自己正在做什么。但是,軟件檢查不必總是手工完成;實際上,有一大堆開源工具和商業工具(我稱之為_軟件檢查器_),可以用它們很方便地對代碼進行靜態分析(這些工具也稱為靜態分析工具)。
使用軟件檢查器,代碼檢查可以通過 Ant 或 Maven 這樣的構建工具來自動完成。通過使用這種自動化,一些低級的源代碼細節(如編碼標準、復雜性和重復程度等)的處理成為了機器的職責。這種職責轉移通過將重點轉向更高級的開發方面(比如設計和長期維護問題)提高了人們手工檢查的效率。
## 關于本系列
作為開發人員,我們的工作就是為用戶提供自動化處理;但是,我們中的許多人卻忽視了自動化自己的開發過程的機會。出于這個目的,_[讓開發自動化](http://www.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&search_by=%E8%AE%A9%E5%BC%80%E5%8F%91%E8%87%AA%E5%8A%A8%E5%8C%96)_ 這一系列的文章專門研究了自動化軟件開發過程的實際應用,并教您_什么時候_ 和_如何_ 成功地應用自動化。
## 用軟件檢查器來解決問題
軟件檢查器有很多,范圍從復雜的商業工具到簡單的開源替代工具。但是,我發現其中三個特別有用,它們是 CheckStyle、CPD 和 JavaNCSS (請參閱 [參考資料](#resources)):
* CheckStyle 報告與項目預定的編碼標準的偏離度。
* CPD 報告代碼重復。
* JavaNCSS 可以幫助團隊專注于更高級的代碼復雜性領域。
所有這些工具都可以免費得到,而且易于集成到 Ant 或 Maven 這樣的構建平臺中,因此它們極易集成到持續集成(CI)系統中。
## 什么是持續集成?
持續集成(CI)是一種實踐,可以讓團隊在_持續的基礎_ 上收到反饋并進行改進,不必等到開發周期后期才尋找和修復缺陷。諸如 CruiseControl 之類的檢查工具是在后臺運行的,它們輪詢版本控制存儲庫,從中尋找更改之處。當發現某一更改時,這類工具就會通過 Ant 執行預定義的構建腳本。持續檢查借助持續集成的實踐得以改進。
### 檢查器樣式標準
我參與了幾家公司的委員會,花了很多時間來定義編碼標準。有一次,我們定義了幾乎_沒人_ 遵守的 25 頁的編碼標準文檔。結果,我們的代碼審查非常痛苦,因為團隊要花時間來挑錯,看看開發人員是否使用了兩個空格規則(與四個空格規則相對),以及是否將參數聲明為 `final`。如果把這些低級檢查工作交給軟件檢查器,我們會節省許多寶貴的時間。
使用自動的源代碼檢查工具(如 CheckStyle)可以提供執行可配置規則集的簡單途徑,規則集可以根據項目的編碼標準而定。此外,可以通過 CheckStyle 插件在 IDE 中運行 CheckStyle,CheckStyle 通過它的 Ant 任務或 Maven 插件直接集成到構建腳本中。CheckStyle 發現的所有規則偏離都會以報告的形式顯示,清楚地指出哪一個文件違規。除此之外,還可以將 Ant 和 Maven 配置成與 CheckStyle 相呼應,這樣,在違反規則時,構建工作就會失敗。
例如,清單 1 演示了 Ant 構建腳本的一個代碼段,它運行 CheckStyle 生成 HTML 報告。請注意 `failOnViolation` 屬性,在這里它被設為 `false`。如果將該屬性設為 `true`,則在發現_任何_ 源代碼違規時,構建都會失敗。
##### 清單 1\. 將 CheckStyle 用于 Ant
```
<taskdef resource="checkstyletask.properties" classpath="${checkstyle.jar}"/>
<checkstyle config="${basedir}/cs-rules.xml" failOnViolation="false">
<formatter toFile="${checkstyle.data.file}" type="xml" />
<fileset casesensitive="yes" dir="${src.dir}" includes="**/*.java" />
</checkstyle>
<xslt taskname="checkstyle"
in="${checkstyle.data.file}"
out="${checkstyle.report.file}"
style="${checkstyle.xsl.file}" />
```
在清單 1 中,`config` 被設置成 `cs-rules.xml`,這表示將根據源代碼目錄(在 `fileset dir` 屬性中)以遞歸的方式運行規則文件。`xslt` 任務接受根據 `formatter toFile` 屬性生成的文件。該任務使用 XSL 文件 checkstyle.xsl 將清單 1 生成的 XML 轉換成一個可讀的 HTML 文件,XSL 文件 checkstyle.xsl 包含在 CheckStyle 安裝文件中。
清單 2 是 CheckStyle 規則文件的示例片段。CheckStyle 可以在代碼基上運行 120 多個規則。
##### 清單 2\. 在 cs-rules.xml 文件中定義的 CheckStyle 規則
```
<module name="Checker">
<module name="TreeWalker">
<property name="cacheFile" value="target/checkstyle.cache"/>
<property name="tabWidth" value="4"/>
<module name="ImportOrder">
<property name="ordered" value="true"/>
<property name="separated" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="120"/>
</module>
<module name="FileLength">
<property name="max" value="400"/>
</module>
<module name="UnusedImports"/>
</module>
</module>
```
每個 CheckStyle 規則都是一個模塊。例如,`LineLength` 模塊建立的規則是:所有行中的字符都不得超過 120 個字符,否則 CheckStyle 就生成錯誤。
可以用定制規則擴展 CheckStyle,所以也可以容易地實施編碼規則。而且,它是自動執行的!
圖 1 顯示了使用 Ant 生成的 CheckStyle 報告的一個示例,這是 [清單 1](#listing1) 中所演示的 `xslt` 任務:
##### 圖 1\. CheckStyle HTML 報告

## 用 IDE 怎么樣?
我聽到的一個普遍反應是這些檢查可以在 IDE 中運行。盡管確實如此,_并且極力推薦這樣做_,但是擁有一臺對源代碼運行一組公用規則集(使用 Ant 這樣的工具)的單獨機器的好處是:一致性更好。
### 用 CPD 進行重復性檢查
面向對象編程的一個主要目的是通過創建可以適應不同上下文的可重用對象來促進重用。但是,通常使用的是 “復制-粘貼” 編程,并將它偽裝成 “重用”。有時,出現這種情況是因為開發人員不知道有可重用的組件,而其他時候,則是因為提供了“快速修復”。所以,如果需要進行修改,則必須將更改應用到_所有_ 副本和變體中(當然,如果沒有工具很難完成)。這種手工方式帶來了維護困難并很容易導致遺漏,甚至會帶來缺陷。
CPD 是流行的開源靜態分析工具 PMD 的一部分,它報告代碼基中重復行的數量。此外,CPD 的標志閾值是可配置的,這意味著可以修改 CPD 建議的重復行數。例如,如果將閾值設置成 100 個標志,CPD 就會在_至少_ 重復了 100 個標志的時候顯示一個實例。不過請記住,源代碼總是會包含一些重復部分;CPD 只是為團隊提供了一種調查_值得考慮的代碼重復_ 領域的手段,在這個領域內,可以采取糾正措施,例如有目的的重構。
清單 3 演示將 CPD 用于 Ant 的實際使用。CPD 任務需要的第一個選項是 `minimumTokenCount` 屬性,該屬性用于指定要比較的標志的數量(最小值是一個標志)。另外,可以設置 CPD 忽略某些代碼選項,例如標識符和標量。在這個示例中,Ant `fileset` 類型指定了 CPD 文件應該分析哪些文件,以及應該忽略哪些文件。
##### 清單 3\. 將 CPD 用于 Ant
```
<taskdef name="cpd"
classname="net.sourceforge.pmd.cpd.CPDTask" classpathref="pmd.classpath" />
<cpd minimumTokenCount="100"
format="xml"
language="java"
ignoreIdentifiers="true"
ignoreLiterals="true"
outputFile="${basedir}/cpd.xml">
<fileset dir="${src.dir}">
<include name="**/*.java" />
<include name="**/*.jsp" />
<include name="**/*.xml" />
<exclude name="**/*Test*.java" />
</fileset>
</cpd>
```
如圖 2 所示,cpd.xml 是用清單 3 中的 `outputFile` 屬性產生的輸出。以后我還將向 CPD 添加 XSL,以生成 HTML 報告。
##### 圖 2\. 顯示代碼重復違規的 CPD XML 輸出

除了最明顯的復制-粘貼違規之外,在所有地方手工查找重復代碼也非常具有挑戰性。但是,使用諸如 CPD 之類的工具,就可以迅速找出重復代碼,然后對此進行改進。
## 路徑是什么?
源代碼_路徑_ 也叫做流程控制語句,它指示在方法中更改流程的代碼。這類示例包括條件語句(如 `if` 語句),或者循環構造(如 `while` 和 `for`)。
### 用 JavaNCSS 檢查復雜性
許多研究已經證實,隨著方法中路徑數量的增加,理解和維護同一方法的難度也隨之增加。結果,代碼越難以理解和維護,就越容易出現缺陷。
JavaNCSS 是一個免費工具,提供了不同的代碼測量,例如非注釋性源代碼語句(或代碼行)的數量、所有分析過的方法的圈復雜度量值(cyclomatic complexity number)。清單 4 演示了 JavaNCSS 的使用,可以看到,它不是很難使用。只要將 JavaNCSS 指向一個包含源代碼的目錄,讓它自己處理就行了!
##### 清單 4\. 將 JavaNCSS 用于 Ant
```
<path id="javancss.classpath">
<fileset dir="${lib.dir}" />
</path>
<taskdef name="javancss" classpathref="javancss.classpath"
classname="javancss.JavancssAntTask">
</taskdef>
<javancss srcdir="${src.dir}"
generateReport="true"
outputfile="${javancss.report.dir}/javancss_metrics.xml"
format="xml"/>
```
像多數軟件檢查器一樣,這里生成了一個 XML 文件,可以通過 XSLT 很容易地將它轉換成一個 HTML 報告,就像圖 3 中所做的那樣:
##### 圖 3\. JavaNCSS HTML 報告

理解應用程序代碼復雜性可沒有那么容易。實際上,在某些情況下,復雜性的值可能會令人誤解。例如,有些工具為 `switch` 語句提供較高的值。但是我發現,使用 JavaNCSS 這樣的工具,有助于降低高復雜性的領域,最終提高代碼的可理解性和可維護性。
* * *
## 對每個更改都進行檢查,將缺陷控制在限度內
每當團隊成員向版本控制存儲庫提交更改時,代碼就會發生改變。但它是怎么改變的呢?被修改的代碼是受到復制-粘貼工作的影響嗎?復雜性會增加嗎?了解這些的惟一方式就是在_每次簽入_ 時都運行軟件檢查器。此外,在持續的基礎上接受目前為止討論過的每種風險的反饋,這是一種_自動_ 讓代碼基進行健康檢查的一種可靠方式!
### Cruise 與 CI
CruiseControl 是 Java? 社區使用最廣的開源 CI 工具之一。這個工具被配置成在后臺運行,用于檢索版本控制存儲庫,例如 CVS。在發現源代碼更改時(例如,有人簽入了代碼),CruiseControl 就會執行源代碼簽出,并運行預定義的構建腳本。
所以,_只要_ 版本控制存儲庫中發生了變化,團隊就可以運行 CheckStyle、CPD 和 JavaNCSS 這樣的軟件檢查器。這種能力允許團隊長時間地進行監視和執行檢查,通過使用許多 CI 工具,團隊可以使用報告、電子郵件甚至使用 Ambient Orb 這樣的設備生成報告(參見 [圖 5](#fig5))。
清單 5 演示了使用 CruiseControl 的配置文件(通常名為 config.xml)在 CruiseControl 控制板上顯示 CheckStyle 報告的結果。其他軟件檢查器使用類似的語法將結果合并到 CruiseControl 儀表板中。
##### 清單 5\. 在 config.xml 中用 CruiseControl 記錄 CheckStyle
```
<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
</listeners>
<modificationset quietperiod="30">
<svn RepositoryLocation="http://www.qualitylabs.org/svn/ambientorb"
username="[username]"
password="[password]"
/>
</modificationset>
<schedule interval="60">
<ant anthome="apache-ant-1.6.5" buildfile="build-${project.name}.xml"/>
</schedule>
<log>
<merge dir="merge dir="checkout/${project.name}/_reports/checkstyle" />
</log>
```
圖 4 顯示了用 CruiseControl 和 CheckStyle 生成的示例報告。請注意,可以配置 CruiseControl,顯示其他工具(像 CPD 和 JavaNCSS)的報告,而且通過_對每個源代碼更改_ 都運行這些報告,團隊可以實時地積極改進代碼,不必等到周期末期。
##### 圖 4\. 與 CruiseControl 集成的 CheckStyle 報告

### Ambient 反饋
對于使用 CI 工具持續運行軟件檢查器而言,最酷的事就是團隊有了無數任意使用的通知機制。有時,構建可能并沒有失敗,但是_有些事_ 的變化要求早些而不是晚些采取糾正行動。例如,實際上可以很容易地配置一個設備(就像 Ambient Orb),在代碼復雜度有所上升時,或者在違反一定數量的代碼標準時,使用該設備改變顏色。
清單 6 使用了 Ambient Orb Ant 任務和 Ruby 腳本,在 20 個以上的類超過 300 個源代碼行(SLOC)時,就改變 Orb 的顏色和動畫。在這個示例中,我選擇在類滿足條件時將 orb 的顏色改成 `magenta`,將動畫改成 `crescendo`。
##### 清單 6\. 委托 Ant 構建文件處理 CruiseControl 和 Ambient Orb
```
<target name="checkSloc" >
<exec dir="${basedir}" executable="cmd.exe">
<arg line="/c ${config.dir}/javancss/SlocThreshold.rb
${reports.javancss.dir}/javancss_metrics.xml 20 ${javancss.file}"/>
</exec>
<available file="${basedir}/${javancss.file}" property="sloc.exceeded"/>
<antcall target="notifyOrb" />
</target>
<target name="notifyOrb" if="sloc.exceeded">
<taskdef classname="org.qualitylabs.ambientorb.ant.OrbTask"
name="orb" classpathref="orb.class.path"/>
<orb query="http://myambient.com:8080/java/my_devices/submitdata.jsp"
deviceId="AAA-9A9-AA9"
colorPass="green"
colorFail="magenta"
animationPass="none"
animationFail="crescendo"
commentFail="SLOC+Exceeded" />
</target>
```
圖 5 演示了代碼基中有太多較大的類時,orb 看起來的樣子:
##### 圖 5\. Ambient Orb

使用這種 Ambient Orb 通知方法,就不會收到無窮盡的電子郵件,只要看一眼 _SLOC Threshold Orb_,就可以快速了解我有多少個大型類,它們提醒我要重定向重構工作。當然,組合 ambient orb 與軟件監視器(稍帶些創意)也會創造無窮的可能性!
* * *
## 在合適的時候自動化
請記住,可以選擇許多不同的軟件檢查器。雖然這里分析了三個我喜歡的軟件檢查器,但我還是被 JDepend、PMD 和 FindBugs 工具(后兩個檢查器在 developerWorks 上已詳細討論過)所打動。一旦開始持續檢查過程,插入一個新的檢查器只是幾分鐘的事而已。
持續檢查不會也不應該消除手工軟件檢查;但是,通過對版本控制存儲庫中的每個更改都運行一套自動檢查工具,_將要執行的_ 這些手工檢查會帶來更高的生產率和效率。而且,持續檢查帶來的額外好處有:風險在每一步上都可以降低,而不必等到項目后期。
下一個月,我將深入研究一些更有趣的持續集成工具,例如 CruiseControl、Luntbuild 和 Continuum,并讓您確定哪一個工具最適合您。自動化_從未_ 這么有趣過!
* * *
## 下載
| 描述 | 名字 | 大小 |
| --- | --- | --- |
| Software Inspection examples in Ant | [j-ap08016.zip](http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=155371&filename=j-ap08016.zip&method=http&locale=zh_CN) | 2MB |
- 讓開發自動化
- 讓開發自動化: 部署自動化模式,第 2 部分
- 讓開發自動化: 部署自動化模式,第 1 部分
- 讓開發自動化: 使用基于向導的安裝程序
- 讓開發自動化: 針對廣大開發人員的并行開發
- 讓開發自動化: 實現自動化數據庫遷移
- 讓開發自動化: 持續重構
- 讓開發自動化: 文檔化一鍵通
- 讓開發自動化: 利用 Ivy 管理依賴項
- 讓開發自動化: 自動負載測試
- 讓開發自動化: 使用自動化加速部署
- 讓開發自動化: 持續集成反模式
- 讓開發自動化: 斷言架構可靠性
- 讓開發自動化: 持續測試
- 讓開發自動化: 用 Eclipse 插件提高代碼質量
- 讓開發自動化: 除掉構建腳本中的氣味
- 讓開發自動化: 選擇持續集成服務器
- 讓開發自動化: 持續檢查