本文來自LEE的博客,他首先向大家講述怎樣辨別一個項目代碼的好壞、如何區分優秀代碼和腐化代碼,最后給大家講述如何寫出高質量的Java代碼。
代碼質量概述

怎樣辨別一個項目代碼寫得好還是壞?優秀的代碼和腐化的代碼區別在哪里?怎么讓自己寫的代碼既漂亮又有生命力?接下來將對代碼質量的問題進行一些粗略的介紹。也請有代碼質量管理經驗的朋友提出寶貴的意見。
代碼質量所涉及的5個方面:編碼標准、代碼重復、代碼覆蓋率、依賴項分析、復雜度分析。這5個方面很大程序上決定了一份代碼的質量高低。我們分別來看一下這5方面:
- 編碼標准:這個想必都很清楚,每個公司幾乎都有一份編碼規范,類命名、包命名、代碼風格之類的東西都屬於其中。
- 代碼重復:顧名思義就是重復的代碼,如果你的代碼中有大量的重復代碼,你就要考慮是否將重復的代碼提取出來,封裝成一個公共的方法或者組件。
- 代碼覆蓋率:測試代碼能運行到的代碼比率,你的代碼經過了單元測試了嗎?是不是每個方法都進行了測試,代碼覆蓋率是多少?這關系到你的代碼的功能性和穩定性。
- 依賴項分析:你的代碼依賴關系怎么樣?耦合關系怎么樣?是否有循環依賴?是否符合高內聚低耦合的原則?通過依賴項分析可以辨別一二。
- 復雜度分析:以前有人寫的程序嵌套了10層 if else你信嗎?圈復雜度之高,讓人難以閱讀。通過復雜度分析可以揪出這些代碼,要相信越優秀的代碼,越容易讀懂。
上面解釋了代碼質量相關的5個方面,在實際開發環境中,已經有很多工具為我們解決以上5個方面的問題,下列5個eclipse插件分別對這5個問題有很好的支持:
- 編碼標准:CheckStyle 插件
- 代碼重復:PMD的CPD 插件
- 代碼覆蓋率:Eclemma 插件
- 依賴項分析:JDepend 插件
- 復雜度分析:Eclipse Metric 插件
注:某些插件需要科學上網才能更新
1. 編碼標准(CheckStyle的使用)
在Eclipse上安裝好了CheckStyle插件后,我們來建一個類用它跑一下。這個類很簡單,一個常見的用戶實體,包含了ID,用戶名、密碼、郵件等屬性,並包含get set方法,一個標准的POJO。運行CheckStyle檢查一下:

一個我們平時再普通不過的一個類,被CheckStyle弄出這么多問題,情何以堪,我們來看看究竟是什么情況?
看一下這些警告信息:
line 1、,說缺少package-info.java文件。
line 2、,說第一句注釋要以“.”結尾。
line 30、,缺少java doc注釋。
line 35、,getId不是繼承的方法,必須指定abstract,final或空。另外也缺少java doc注釋。
這個類基本就這四類毛病,缺少package-info.java文件,這個文件是做什么的呢?他是用來描述包注釋的類,有一定的特殊性,要想詳細了解請百度。如果對你的項目沒有太大的影響,可以忽略它。配置CheckStyle的方法我們等會再說。第一句注釋要以“.”結尾,這看你的習慣,你確定需要這個,你就保留,不需要就忽略。缺少Java doc,對於Java類的屬性來說,注釋是必要的,所以這個要保留。不是繼承的方法,需要加上final關鍵字,如果你有這個習慣,就保留,反之忽略。
我們這里只是建立了一個最簡單的類用CheckStyle來檢查,隨着你的類代碼越來越多,邏輯越來越復雜,CheckStyle能檢查出來的毛病也越來越多。
常見的CheckStyle錯誤有這些:
缺少類型說明
2.“{” should be on the previous line
“{” 應該位於前一行
3.Methods is missing a javadoc comment
方法前面缺少javadoc注釋
4.Expected @throws tag for “Exception”
在注釋中希望有@throws的說明
5.“.” Is preceeded with whitespace “.”
前面不能有空格
6.“.” Is followed by whitespace“.”
后面不能有空格
7.“=” is not preceeded with whitespace
“=” 前面缺少空格
8.“=” is not followed with whitespace
“=” 后面缺少空格
9.“}” should be on the same line
“}” 應該與下條語句位於同一行
10.Unused @param tag for “unused”
沒有參數“unused”,不需注釋
11.Variable “CA” missing javadoc
變量“CA”缺少javadoc注釋
12.Line longer than 80characters
行長度超過80
13.Line contains a tab character
行含有”tab” 字符
14.Redundant “Public” modifier
冗余的“public” modifier
15.Final modifier out of order with the JSL
suggestionFinal modifier的順序錯誤
16.Avoid using the “.*” form of import
Import格式避免使用“.*”
17.Redundant import from the same package
從同一個包中Import內容
18.Unused import-java.util.list
Import進來的java.util.list沒有被使用
19.Duplicate import to line 13
重復Import同一個內容
20.Import from illegal package
從非法包中 Import內容
21.“while” construct must use “{}”
“while” 語句缺少“{}”
22.Variable “sTest1” must be private and have accessor method
變量“sTest1”應該是private的,並且有調用它的方法
23.Variable “ABC” must match pattern “^[a-z][a-zA-Z0-9]*$”
變量“ABC”不符合命名規則“^[a-z][a-zA-Z0-9]*$”
24.“(” is followed by whitespace
“(” 后面不能有空格
25.“)” is proceeded by whitespace
“)” 前面不能有空格
可以看出CheckStyle檢查出來的問題,大多是編碼規則以及風格上的問題,這是編寫高質量代碼最基本的。值得注意的是,我們將一些優秀的開源代碼用CheckStyle來檢查也會檢查出不少問題,這不能不說這些開源不優秀,而是每個公司組織有自己的編寫規范度,這個度既可以減少程序員的工作量又可以讓代碼的可讀性合格,但這個度不一樣符合CheckStyle的完整標准。所以我們一般使用CheckStyle都不會用他的默認標准,而是通過配置,制定適合自己的編碼規則。
自定義CheckStyle規則:

打開CheckStyle配置,新建一個配置,選擇外部配置文件。在這之前最好導出一個Eclipse自帶的CheckStyle配置文件(sun_checks.xml),然后重命名作為一個外部的配置導進去,這么做的目的是導入之后可以修改相應的配置,達到自定義配置的目的(因為Eclipse自帶的配置是加鎖的,不能修改)。導入之后,點擊右邊的“Configure”進行編輯。
先去掉缺少package-info.java文件的提示:

再將第一句注釋要以“.”結尾這個規則去掉,雙擊“Style javadoc”,將窗口內“checkFirstSentence”勾選去掉。

對於實體類,屬性有了注釋,get set方法也不需要注釋了,雙擊“Method javadoc”將allowMissingPropertyJavadoc勾選中。

“getId不是繼承的方法,必須指定abstract,final或空”,如果你懶得在方法上加“final”,這條規則也可以去掉。

如果你不想每一個參數都加“final”,還需要把參數的final規則去掉:

另外還有一個錯誤“'id' hides a field.”,原因是方法的參數和類里面定義的域重名了,但使用eclipse生成的get set方法都會這樣,所以可以忽略此項。

至此我們再使用checkstyle檢查一篇,發現僅剩下屬性缺少注釋這個警告。

對每個屬性加上java doc注釋,所有問題都清除了。以此類推,解決checkstyle問題的方法就是:1、按規則解決代碼問題;2、如果覺得這個問題對你的項目質量影響不大,則可以忽略它。
2. 代碼重復(PMD的CPD的使用)
對於多人開發的項目,難以避免出現重復代碼的問題,盡管我們盡量對共用的代碼進行了封裝,但隨着需求的增加、人員技術水平差異、溝通不足等原因,重復代碼會越來越多。這不僅嚴重影響代碼質量,也無形中增加了代碼量。
注:精簡的程序和高復用度的代碼是我們一直追求的目標。
PMD的CPD工具就是為檢查重復代碼而生的。右鍵項目--->PMD---->Find Suspect Cut and Paste,執行重復代碼檢查:

檢查出來的重復代碼,可以雙擊查看。然后根據業務邏輯以及代碼特征,決定要不要做封裝、怎么封裝。
3. 代碼覆蓋率(Eclemma的使用)
一份質量合格的代碼,不僅包含功能程序本身也包含了對應的測試代碼,Eclemma插件可以用來統計測試代碼覆蓋整體代碼中的比率,以此來評估代碼的功能性和穩定性。
使用Junit編寫好測試用例之后,右鍵Coverage As--->Junit Test,運行測試用例,Eclemma會統計出相關的代碼覆蓋率:

根據這個結果,你可以看出自己編寫的測試用例覆蓋到了那些代碼,而沒有覆蓋到的代碼,則有可能成為代碼質量的盲區。
4. 依賴項分析(JDepend的使用)
隨着程序業務邏輯的增加,代碼的依賴關系也變的越來越復雜,JDepend插件可以統計包和類的依賴關系,分析出程序的穩定性、抽象性和是否存在循環依賴的問題。右鍵包--->Run JDepend Analysis:

看一下這幾項指標:
- CC(Number of Classes):被分析package的具體和抽象類(和接口)的數量,用於衡量package的可擴展性。如果一個類中實現了其他類,如實現了監聽類,則監聽類的數目也記錄在此。
- AC(Abstract classes):抽象類和接口的數量。
- Ca(Afferent Couplings):依賴於被分析package的其他package的數量,用於衡量pacakge的職責。即有多少包調用了它。
- Ce(Efferent Couplings):被分析package的類所依賴的其他package的數量,用於衡量package的獨立性。即它調用了多少其他包。
- A(Abstractness):被分析package中的抽象類和接口與所在package所有類數量的比例,取值范圍為0-1。
- I(Instability):I=Ce/(Ce+Ca),用於衡量package的不穩定性,取值范圍為0-1。I=0表示最穩定,I=1表示最不穩定。即如果這個類不調用任何其他包,則它是最穩定的。
- D(Distance):被分析package和理想曲線A+I=1的垂直距離,用於衡量package在穩定性和抽象性之間的平衡。理想的package要么完全是抽象類和穩定(x=0,y=1),要么完全是具體類和不穩定(x=1,y=0)。取值范圍為0-1,D=0表示完全符合理想標准,D=1表示package最大程度地偏離了理想標准。即你的包要么全是接口,不調用任何其他包(完全是抽象類和穩定),要么是具體類,不被任何其他包調用。
- Cycle:循環依賴的數量。
有個這個報告我們就可以有針對性的對代碼進行設計和重構。
5. 復雜度分析(Metrics的使用)
對於閱讀代碼的人來說,越簡單的代碼越好理解和維護,如果你的代碼閱讀起來很費勁或者你自己過段時間后再來看都看不懂,你就得想辦法解決下代碼的復雜度問題了。Metrics插件可以幫你做到這點。
首先在Java透視圖下右鍵一個項目---->Properties,選擇Metrics,勾選Enble Metrics。

然后Window--->Show View---->Other---->Metrics View

打開Metrics視圖,點擊右上角運行圖標,即可得到復雜度分析的結果:

可以根據復雜度指標,對自己的程序進行優化。
小結
本文介紹了和java代碼質量相關的5個方面問題,並介紹對應eclipse插件的用法和作用。在我們實際開發中,盡量根據自己公司和團隊的情況來制定一些檢查規則,來提高代碼質量。並且在大多數情況下,會有兩個檢查環節,即本地檢查和持續集成環境的檢查,我們常用的Hudson就可以集成很多插件。