| 每位開發(fā)人員對代碼質(zhì)量的含義都有著自己的看法,并且大多數(shù)人對如何查找編寫欠佳的代碼也有自己的想法。甚至術(shù)語代碼味道(code smell) 也已進(jìn)入大眾詞匯表,成為描述代碼需要改進(jìn)的一種方式。 代碼味道通常由開發(fā)人員直接判定,有趣的是,它是許多代碼注釋綜合在一起的味道。一些人聲稱公正的代碼注釋是好事情,而另一些人聲稱代碼注釋只是解釋過于復(fù)雜的代碼的一種機(jī)制。顯然,Javadocs? 很有用,但是多少內(nèi)嵌注釋才足以維護(hù)代碼?如果代碼已經(jīng)編寫得足夠好,它還需要解釋自己嗎? 這告訴我們,代碼味道是一種評估代碼的機(jī)制,它具有主觀性。我相信,那些聞起來味道糟透了的代碼可能是其他人曾經(jīng)編寫的最好的代碼。以下這些短語聽起來是不是很熟悉? 
 或者 
 我們需要的是客觀評估代碼質(zhì)量的方法,某種可以決定性地告訴我們正在查看的代碼是否存在風(fēng)險的東西。不管您是否相信,這種東西確實存在!用來客觀評估代碼質(zhì)量的機(jī)制已經(jīng)出現(xiàn)了一段時間了,只是大多數(shù)開發(fā)人員忽略了它們。這些機(jī)制被稱為代碼度量 (code metric)。 代碼度量的歷史 幾十年前,少數(shù)幾個非常聰明的人開始研究代碼,希望定義一個能夠與缺陷關(guān)聯(lián)的測量系統(tǒng)。這是一個非常有趣的主張:通過研究帶 bug 代碼中的模式,他們希望創(chuàng)建正式的模型,然后可以評估這些模型,在缺陷成為缺陷之前 捕獲它們。 在這條研究之路上,其他一些非常聰明的人也決定通過研究代碼看看他們是否可以測量開發(fā)人員的生產(chǎn)效率。對每位開發(fā)人員的代碼行的經(jīng)典度量似乎只停留在表面上: 
 但是這種生產(chǎn)率度量在實踐中是非常令人失望的,主要是因為它容易被濫用。一些代碼測量包括內(nèi)嵌注釋,并且這種度量實際上受益于剪切粘貼式開發(fā) (cut-and-paste style development)。 
 可以預(yù)見,生產(chǎn)率研究被證實是非常不準(zhǔn)確的,但在管理團(tuán)隊 (management body) 廣泛使用這種生產(chǎn)率度量以期了解每個人的能力的價值之前,情況并非如此。來自開發(fā)人員社區(qū)的痛苦反應(yīng)是有理由的,對于一些人而言,那種痛苦感覺從未真正走遠(yuǎn)。 未經(jīng)雕琢的鉆石 盡管存在這些失敗,但在那些復(fù)雜度與缺陷的相互關(guān)系的研究中仍然有一些美玉。大多數(shù)開發(fā)人員忘記進(jìn)行代碼質(zhì)量研究已有很長一段時間了,但對于那些仍正在鉆研的人而言(特別是如果您也正在為追求代碼質(zhì)量而努力鉆研),會在今天的應(yīng)用中發(fā)現(xiàn)這些研究的價值。例如,您曾注意到一些長的方法有時難以理解嗎?是否曾無法理解嵌套很深的條件從句中的邏輯?您的避開這類代碼的本能是正確的。一些長的方法和帶有大量路徑的方法是 難以理解的,有趣的是,這類方法容易導(dǎo)致缺陷。 我將使用一些例子展示我要表達(dá)的意思。 數(shù)字的海洋 研究顯示,平均每人在其大腦中大約能夠處理 7(±2)位數(shù)字。這就是為什么大多數(shù)人可以很容易地記住電話號碼,但卻很難記住大于 7 位數(shù)字的信用卡號碼、發(fā)射次序和其他數(shù)字序列的原因。 此原理還可以應(yīng)用于代碼的理解上。您以前大概已經(jīng)看到過類似清單 1 中所示的代碼片段: 清單 1. 適用記憶數(shù)字的原理 清單 1 展示了 9 條不同的路徑。該代碼片段實際上是一個 350 多行的方法的一部分,該方法展示了 41 條不同的路徑。設(shè)想一下,如果您被分配一項任務(wù),要修改此方法以添加一項新功能。如果您該方法不是您編寫的,您認(rèn)為您能只做必要的更改而不會引入任何缺陷嗎? 當(dāng)然,您應(yīng)該編寫一個測試用例,但您會認(rèn)為該測試用例能將您的特定更改在條件從句的海洋中隔離起來嗎? 測量路徑復(fù)雜度 圈復(fù)雜度 是在我前面提到的那些研究期間開創(chuàng)的,它可以精確地測量路徑復(fù)雜度。通過利用某一方法路由不同的路徑,這一基于整數(shù)的度量可適當(dāng)?shù)孛枋龇椒◤?fù)雜度。實際上,過去幾年的各種研究已經(jīng)確定:圈復(fù)雜度(或 CC)大于 10 的方法存在很大的出錯風(fēng)險。因為 CC 通過某一方法來表示路徑,這是用來確定某一方法到達(dá) 100% 的覆蓋率將需要多少測試用例的一個好方法。例如,以下代碼(您可能記得本系列的第一篇文章中使用過它)包含一個邏輯缺陷: 清單 2. PathCoverage 有一個缺陷! 作為響應(yīng),我可以編寫一個測試,它將達(dá)到 100% 的行覆蓋率: 清單 3. 一個測試產(chǎn)生完全覆蓋! 接下來,我將運行一個代碼覆蓋率工具,比如 Cobertura,并將獲得如圖 1 中所示的報告: 圖 1. Cobertura 報告 哦,有點失望。代碼覆蓋率報告指示 100% 的覆蓋率,但我們知道這是一個誤導(dǎo)。 二對二 注意,清單 2 中的 pathExample() 方法有一個值為 2 的 CC(一個用于默認(rèn)路徑,一個用于 if 路徑)。使用 CC 作為更精確的覆蓋率測量尺度意味著第二個測試用例是必需的。在這里,它將是不進(jìn)入 if 條件語句而采用的路徑,如清單 4 中的 testPathExampleFalse() 方法所示: 清單 4. 沿著較少采用的路徑向下 正如您可以看到的,運行這個新測試用例會產(chǎn)生一個令人討厭的 NullPointerException。在這里,有趣的是我們可以使用圈復(fù)雜度而不是 使用代碼覆蓋率來找出這個缺陷。代碼覆蓋率指示我們已經(jīng)在一個測試用例之后完成了此操作,但 CC 卻會強(qiáng)迫我們編寫額外的測試用例。不算太壞,是吧? 幸運的是,這里的測試中的方法有一個值為 2 的 CC。設(shè)想一下該缺陷被隱藏在 CC 為 102 的方法中的情況。祝您好運找到它! 圖表上的 CC Java 開發(fā)人員可使用一些開放源碼工具來報告圈復(fù)雜度。其中一個這樣的工具是 JavaNCSS,它通過檢查 Java 源文件來確定方法和類的長度。此外,此工具還收集代碼庫中每個方法的圈復(fù)雜度。通過利用 Ant 任務(wù)或 Maven 插件配置 JavaNCSS,可以生成一個列出以下內(nèi)容的 XML 報告: 
 該工具附帶了少量樣式表,可以使用它們來生成總結(jié)數(shù)據(jù)的 HTML 報告。例如,圖 2 闡述了 Maven 生成的報告: 圖 2. Maven 生成的 JavaNCSS 報告 此報告中帶有 Top 30 functions containing the most NCSS 標(biāo)簽的部分詳細(xì)描述了代碼庫中最長的方法,順便提一句,該方法幾乎總是 與包含最大圈復(fù)雜度的方法相關(guān)聯(lián)。例如,該報告列出了 DBInsertQueue 類的 updatePCensus() 方法,因為此方法的非注釋行總數(shù)為 283,圈復(fù)雜度(標(biāo)記為 CCN)為 114。 正如上面所演示的,圈復(fù)雜度是代碼復(fù)雜度的一個好的指示器;此外,它還是用于開發(fā)人員測試的一個極好的衡量器。一個好的經(jīng)驗法則是創(chuàng)建數(shù)量與將被測試代碼的圈復(fù)雜度值相等的測試用例。在圖 2 中所見的 updatePCensus() 方法中,將需要 114 個測試用例來達(dá)到完全覆蓋。 分而治之 在面對指示高圈復(fù)雜度值的報告時,第一個行動是檢驗所有相應(yīng)測試的存在。如果存在一些測試,測試的數(shù)量是多少?除了極少數(shù)代碼庫以外,幾乎所有代碼庫實際上都有 114 個測試用例用于 updatePCensus() 方法(實際上,為一個方法編寫如此多的測試用例可能會花費很長時間)。但即使是很小的一點進(jìn)步,它也是減少方法中存在缺陷風(fēng)險的一個偉大開始。 如果沒有任何相關(guān)的測試用例,顯然需要測試該方法。您首先想到的可能是:到重構(gòu)的時間了,但這樣做將打破第一個重構(gòu)規(guī)則,即將編寫一個測試用例。先編寫測試用例會降低重構(gòu)中的風(fēng)險。減少圈復(fù)雜度的最有效方式是隔離代碼部分,將它們放入新的方法中。這會降低復(fù)雜度,使方法更容易管理(因此更容易測試)。當(dāng)然,隨后應(yīng)該測試那些更小的方法。 在持續(xù)集成環(huán)境中,隨時間變化 評估方法的復(fù)雜度是有可能的。如果是第一次運行報告,那么您可以監(jiān)視方法的復(fù)雜度值或任何相關(guān)的成長度(growth)。如果在 CC 中看到一個成長度,那么您可以采取適當(dāng)?shù)膭幼鳌?/p> 如果某一方法的 CC 值在不斷增長,那么您有兩個響應(yīng)選擇: 
 還要注意的是,JavaNCSS 不是惟一用于 Java 平臺促進(jìn)復(fù)雜度報告的工具。PMD 是另一個分析 Java 源文件的開源項目,它有一系列的規(guī)則,其中之一就是報告圈復(fù)雜度。CheckStyle 是另一個具有類似的圈復(fù)雜度規(guī)則的開放源碼項目。PMD 和 CheckStyle 都有 Ant 任務(wù)和 Maven 插件 使用復(fù)雜度度量 因為圈復(fù)雜度是如此好的一個代碼復(fù)雜度指示器,所以測試驅(qū)動的開發(fā) (test-driven development) 和低 CC 值之間存在著緊密相關(guān)的聯(lián)系。在編寫測試時(注意,我沒有暗示是第一次),開發(fā)人員通常傾向于編寫不太復(fù)雜的代碼,因為復(fù)雜的代碼難以測試。如果您發(fā)現(xiàn)自己難以編寫某一代碼,那么這是一種警示,表示正在測試的代碼可能很復(fù)雜。在這些情況下,TDD 的簡短的 “代碼、測試、代碼、測試” 循環(huán)將導(dǎo)致重構(gòu),而這將繼續(xù)驅(qū)使非復(fù)雜代碼的開發(fā)。 所以,在使用遺留代碼庫的情況下,測量圈復(fù)雜度特別有價值。此外,它有助于分布式開發(fā)團(tuán)隊監(jiān)視 CC 值,甚至對具有各種技術(shù)級別的大型團(tuán)隊也是如此。確定代碼庫中類方法的 CC 并連續(xù)監(jiān)視這些值將使您的團(tuán)隊在復(fù)雜問題出現(xiàn)時 搶先處理它們。 | 
|  |