程式碼質量工具總結
說到程式碼質量,這個是程式設計師職業生涯,至少是編碼生涯的永久性話題;不同時期,見解也有不同;個人認為,不存在什麼絕對錯誤和絕對正確,當然那種寫出明顯的空指標異常排除在外;有時候編碼質量是一個見仁見智,個人的習慣問題。但是,編碼也是群體活動,有個共同遵守的規範是必須的,也是必要的。
寫在前面,本文所述的所有的工具,基本上都是考慮到 java 語言層面,當然這些工具也可能適用其他語言。
PMD
程式碼庫一直在持續更新的程式碼質量檢查工具。提供各大 IDE 外掛,提供 maven-pmd-plugin
外掛。最新版的 java 語言質量檢查規則:https://pmd.github.io/pmd-6.8.0/pmd_rules_java.html
檢查規則:
- If、While、IfElse、For表示式必須使用{},無論有多少語句;
- 如果方法返回boolean,那麼注意避免不必要的if…then…else語句;
- 避免if語句巢狀過深(降低程式碼可讀性);
解決辦法:建議if巢狀不要超過2層。使用工具方法封裝更多的if語句或者把巢狀的if表示式放到同一個層次中; - 忽略大小寫進行字串比較時,使用String.equalsIgnoreCase,不要使用String.toLowerCase。前者有更好的效能,而且還可以避免後者帶來的本地化問題;
- 避免方法級的同步,塊級別的同步可以確保內含真正需要同步的程式碼;
- 使用集合類的isEmpty方法;
java.util.Collection類的isEmpty方法提供判斷一個集合類是否包含元素。不要是使用size()和0比較來重複類庫已經提供的方法。 這條原則告訴我們一個普遍的原則:複用。儘量複用,充分利用已有的資源,不要重複自己。 - 沒有使用的就去掉,保持程式碼的乾淨、整潔。沒有使用的程式碼包括:沒有使用的私有成員、本地變數、私有方法、方法引數(引數定義,但方法內沒有使用此引數)
- 構建StringBuffer或StringBuilder時,如果知道長度,請指定,這樣效能更好。不指定,則預設長度是16,這樣當長度不夠時,就會有擴容的動作。
- 如果本地變數只被賦值一次,那麼把它宣告為Final;如果方法引數從來不會被重新賦值,那麼把它宣告為Final;
- 如果想由陣列構建List,請使用Arrays.asList;
- 陣列複製,請使用System.arraycopy,別用迴圈;
""+123
的方式把數字轉換為String,不夠高效;- 避免程式碼中出現各種"空"的語句:空Catch,空If,空while,空try,空finally,空switch,空Synchronized塊,空static塊;
- 重複的程式碼:拷貝/貼上程式碼意味著拷貝/貼上bugs;
- 迴圈體建立新物件:儘量不要再for或while迴圈體內例項化一個新物件;
- 資源關閉:Connect,Result,Statement等使用之後確保關閉掉
還有其他很多……
使用者還可以自定義規則,檢查Java程式碼是否符合某些特定的編碼規範。例如,你可以編寫一個規則,要求PMD找出所有建立Thread和Socket物件的操作。
工作原理
TODO
FindBugs
使用靜態分析方法標識出Java程式中上百種潛在的不同型別的錯誤。
checkstyle
CheckStyle檢查程式碼是否符合制定的規範。CheckStyle 檢查是基於原始碼的,所以不需要編譯,執行速度快。CheckStyle有針對不同IDE和構建工具的各種外掛,如 maven-checkstyle-plugin
外掛,其簡單的參考配置:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<!--內建4種規範:config/sun_checks.xml、config/maven_checks.xml、config/turbine_checks.xml、config/avalon_checks.xml、其中sun_checks.xml為預設值。修改預設配置-->
<configLocation>config/maven_checks.xml</configLocation>
<!--啟用自定義規範檔案-->
<!--<configLocation>${basedir}/src/config/custom_checkstyle.xml</configLocation>-->
</configuration>
<executions>
<execution>
<id>checkstyle</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
<configuration>
<failOnViolation>true</failOnViolation>
</configuration>
</execution>
</executions>
</plugin>
定義在maven lifecycle的validate階段執行check task,並且如果發現有違反標準的情況就會fail當前的build。執行checkstyle檢查:mvn checkstyle:checkstyle
跳過對指定檔案的某些檢查
suppression:忽略指定檔案的問題檢查,不推薦;方法是建立一個checkstyle-suppressions.xml檔案:
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.0//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_0.dtd">
<suppressions>
<suppress checks="LineLengthCheck"
files="SessionMessageSource.java"/>
</suppressions>
<suppressionsLocation>${basedir}/src/config/checkstyle-suppressions.xml</suppressionsLocation>
然後在配置檔案裡面可以定義一系列可用的模組,每一個模組提供嚴格程度(強制的,可選的…)可配置的檢查規則。規則可以觸發通知(notification),警告(warning)和錯誤(error)。
附:google-checkstyle
SonarLint & SonarQube
必須要指出 SonarQube 是開源的程式碼質量管理工具,web 頁面平臺形式視覺化程式碼質量, SonarLint 是SonarQube 提供的外掛,可以安裝在各個 IDE 工具。
SonarQube 覆蓋程式碼質量的多個方面,更新版本的功能更多:
特性:
- 通過外掛擴充套件
支援新的程式語言、新增規則引擎、計算更復雜的度量指標,這些都可以藉助強大的外掛擴充套件機制。通過外掛機制,Sonar 可以整合不同的測試工具,程式碼分析工具,以及持續整合工具,比如pmd-cpd、checkstyle、findbugs、Jenkins。通過不同的外掛對這些結果進行再加工處理,通過量化的方式度量程式碼質量的變化,從而可以方便地對不同規模和種類的工程進行程式碼質量管理。 - 支援多種程式語言
藉助外掛,支援各種主流程式語言; - 整合CI
同時 Sonar 還對大量的持續整合工具提供介面支援,可以很方便地在持續整合中使用 Sonar - 程式碼質量周邊
SonarQube是一個Web應用程式(B/S)。規則、警告、例外、設定……都可以線上配置。通過自己的資料庫,SonarQube不僅僅是展示各項指標的綜合結果,同時也結合歷史質量資料。 - 國際化
對國際化以及報告文件化也有良好的支援。
alibaba coding guiding
通過外掛形式,可以按照在 IDE 中。有一份詳細的 pdf 文件解釋;針對外掛檢查出來的不規範,有詳細的解釋。
實施
上面講了不少工具,可是規範畢竟是規範,不實施起來,強硬提交(–force or --hard)也是無奈。所以有必要設定一個關卡,不滿足則不讓提交或者失敗;
VCS 服務端
在 version control system 服務端(如git,svn服務端)實施,如果不達標則拒絕提交(允許提交到其他分支以便保留工作成果,但是不能 merge 到 master 或者 release branch)。
提交程式碼前自動執行檢查,想到Git commit hook,比如 pre-commit hook
#!/bin/sh
# From gist at https://gist.github.com/chadmaughan/5889802
# stash any unstaged changes
git stash -q --keep-index
# run the tests with the gradle wrapper
./gradlew clean build
# store the last exit code in a variable
RESULT=$?
# unstash the unstashed changes
git stash pop -q
# return the './gradlew build' exit code
exit $RESULT
將該指令碼拷貝到專案 .git/hooks/下,在執行git commit的時自動觸發檢查,如果檢查失敗則提交失敗。.git 並不能提交到遠端程式碼倉庫,除人工分發和拷貝外,有沒有更好的方式在團隊中共享這個機制呢?
可以把pre-commit納入版本控制(如config/pre-commit),再使用構建工具的擴充套件機制來自動完成拷貝工作,這樣可以間接實現git hooks的團隊間共享。缺點:沒有使用 gradle,使用 maven 怎麼做?
# build.gradle
task installGitHooks(type: Copy) { //將pre-commit拷貝到指定位置
from new File(rootProject.rootDir, 'config/pre-commit')
into {
new File(rootProject.rootDir, '.git/hooks')
}
fileMode 0755
}
build.dependsOn installGitHooks //設定執行build任務時會自動觸發installGitHooks任務
CI/CD 服務端
利用 CI/CD 服務端,在 Jenkins 構建(Jenkins 整合 checkstyle 或者 sonarqube)或者隨後的部署階段檢查程式碼質量,不滿足則觸發失敗。或者如果使用 maven-checkstyle-plugin
,則可以隨時觸發檢查。
參考
1