夏眠鱼

Mar 09, 2017

提高代码审查的效率

前言

CheckStyle 是一个帮助 Java 开发人员遵守代码规范的工具,它能够自动化代码规范检查过程,从而使得开发人员从这项重要但枯燥的任务中解脱。

我们会发现,GitHub 上很多开源项目都有个 checkstyle.xml,目的是希望大家能够准守约定的代码规范去贡献代码。所以 CheckStyle 的出现,主要是服务于多人协作的项目,如果你们的团队也很注重代码规范且人工审查它,那就赶紧用上 CheckStyle 吧。

CheckStyle 检验的内容

  • Javadoc 注释
  • 命名约定
  • 标题
  • import 语句
  • 体积大小
  • 空白
  • 修饰符
  • 代码问题
  • 类设计
  • 混合检查

定制 CheckStyle.xml

下面是根据我所在团队的代码规范定制的,每个团队可能不一样,更多的 CheckStyle 配置可看官网的说明。

所有的 <module/> 都有对应的 <message/><message/> 是不符合规范的代码的提示语,可自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">

<property name="severity" value="warning"/>

<!-- tab 制表符-->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
<message key="name.containsTab" value="文件中不能含有 tab 制表符。"/>
</module>

<module name="TreeWalker">

<!-- import 检查,参考:http://checkstyle.sourceforge.net/config_import.html -->
<!-- 限制导入 .* 包 -->
<module name="AvoidStarImport"/>

<!-- 限制导入多余的包 -->
<module name="RedundantImport"/>

<!-- 限制导入未使用过的包 -->
<module name="UnusedImports"/>


<!-- Javadoc 检查,参考:http://checkstyle.sourceforge.net/config_javadoc.html -->
<!-- 类的 Javadoc 注释-->
<module name="JavadocType">
<property name="tokens" value="CLASS_DEF"/>
<property name="allowUnknownTags" value="true"/>
<message key="javadoc.missing" value="类缺少 Javadoc 注释。"/>
</module>

<!-- 接口的 Javadoc 注释-->
<module name="JavadocType">
<property name="tokens" value="INTERFACE_DEF"/>
<property name="allowUnknownTags" value="true"/>
<message key="javadoc.missing" value="接口缺少 Javadoc 注释。"/>
</module>

<!-- 枚举的 Javadoc 注释-->
<module name="JavadocType">
<property name="tokens" value="ENUM_DEF"/>
<property name="allowUnknownTags" value="true"/>
<message key="javadoc.missing" value="枚举缺少 Javadoc 注释。"/>
</module>

<!-- 注解的 Javadoc 注释-->
<module name="JavadocType">
<property name="tokens" value="ANNOTATION_DEF"/>
<property name="allowUnknownTags" value="true"/>
<message key="javadoc.missing" value="注解缺少 Javadoc 注释。"/>
</module>


<!-- 修饰符检查,参考:http://checkstyle.sourceforge.net/config_modifiers.html -->
<!-- 修饰符顺序 -->
<module name="ModifierOrder">
<message key="mod.order"
value="修饰符需遵循如下顺序:public、protected、private、abstract、
static、final、transient,volatile、synchronized、native、strictfp。"/>
</module>


<!-- 代码块检查,参考:http://checkstyle.sourceforge.net/config_blocks.html -->
<!-- 起始大括号 -->
<module name="LeftCurly">
<message key="line.previous" value="起始大括号的命名规范:''{0}'' 应在前一行。"/>
</module>
<module name="RightCurly"/>


<!-- 空格检查,参考:http://checkstyle.sourceforge.net/config_whitespace.html -->
<!-- 泛型 -->
<module name="GenericWhitespace">
<message key="ws.followed" value="泛型的命名规范:''{0}'' 后不应有空格。"/>
<message key="ws.preceded" value="泛型的命名规范:''{0}'' 前不应有空格。"/>
</module>

<!-- 方法参数 -->
<module name="ParenPad">
<message key="ws.followed" value="方法参数的命名规范:''{0}'' 后不应有空格。"/>
<message key="ws.preceded" value="方法参数的命名规范:''{0}'' 前不应有空格。"/>
</module>

<!-- 方法 -->
<module name="MethodParamPad">
<message key="ws.preceded" value="方法的命名规范:''{0}'' 前不应有空格。"/>
</module>

<!-- 类型转换 -->
<module name="TypecastParenPad">
<message key="ws.followed" value="类型转换的命名规范:''{0}'' 后不应有空格。"/>
<message key="ws.preceded" value="类型转换的命名规范:''{0}'' 前不应有空格。"/>
</module>


<!-- 命名规范检查,参考:http://checkstyle.sourceforge.net/config_naming.html -->
<!-- 包名 -->
<module name="PackageName">
<message key="name.invalidPattern" value="包名的命名规范:全部小写。"/>
</module>

<!-- 类名 -->
<module name="TypeName">
<property name="tokens" value="CLASS_DEF"/>
<message key="name.invalidPattern" value="类的命名规范:单个大写字母。"/>
</module>

<!-- 接口名 -->
<module name="TypeName">
<property name="tokens" value="INTERFACE_DEF"/>
<message key="name.invalidPattern" value="接口的命名规范:单个大写字母。"/>
</module>

<!-- 枚举名 -->
<module name="TypeName">
<property name="tokens" value="ENUM_DEF"/>
<message key="name.invalidPattern" value="枚举的命名规范:单个大写字母。"/>
</module>

<!-- 注解名 -->
<module name="TypeName">
<property name="tokens" value="ANNOTATION_DEF"/>
<message key="name.invalidPattern" value="注解的命名规范:单个大写字母。"/>
</module>

<!-- 类的类型参数 -->
<module name="ClassTypeParameterName">
<message key="name.invalidPattern" value="类的类型参数的命名规范:单个大写字母。"/>
</module>

<!-- 接口的类型参数 -->
<module name="InterfaceTypeParameterName">
<message key="name.invalidPattern" value="接口的类型参数的命名规范:单个大写字母。"/>
</module>

<!-- 方法的类型参数 -->
<module name="MethodTypeParameterName">
<message key="name.invalidPattern" value="方法的类型参数的命名规范:单个大写字母。"/>
</module>

<!-- 常量-->
<module name="ConstantName">
<message key="name.invalidPattern" value="常量的命名规范:大写字母或下划线。"/>
</module>

<!-- 静态变量 -->
<module name="StaticVariableName">
<property name="format" value="^(s|is)[A-Z][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="静态变量的命名规范:s前缀+大写驼峰(布尔型的变量可使用 is 前缀)。"/>
</module>

<!-- 成员变量 -->
<module name="MemberName">
<property name="format" value="^(m|is)[A-Z][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" value="成员变量的命名规范:m前缀+大写驼峰(布尔型的变量可使用 is 前缀)。"/>
</module>

<!-- 方法名-->
<module name="MethodName">
<message key="name.invalidPattern" value="方法名的命名规范:小写驼峰。"/>
</module>

<!-- 方法参数 -->
<module name="ParameterName">
<message key="name.invalidPattern" value="方法参数的命名规范:小写驼峰。"/>
</module>

<!-- 局部的 final 类型变量 -->
<module name="LocalFinalVariableName">
<message key="name.invalidPattern" value="局部的 final 类型变量的命名规范:小写驼峰。"/>
</module>

<!-- 局部的非 final 类型变量 -->
<module name="LocalVariableName">
<message key="name.invalidPattern" value="局部的非 final 类型变量的命名规范:小写驼峰。"/>
</module>


<!-- 大小长度检查,参考:http://checkstyle.sourceforge.net/config_sizes.html -->
<!-- 根类数量 -->
<module name="OuterTypeNumber">
<message key="maxOuterTypes" value="一个文件只能有一个根类。"/>
</module>

<!-- 内部类数量 -->
<module name="AnonInnerLength">
<property name="max" value="60"/>
<message key="maxLen.anonInner" value="内部类过长或数量过多,请将内部类抽离成独立类。"/>
</module>

<!-- 每个类型的方法数量 -->
<module name="MethodCount">
<property name="maxTotal" value="30"/>
<message key="too.many.methods" value="方法数量过多,请遵守单一职责设计原则。"/>
</module>

<!-- 方法的参数数量 -->
<module name="ParameterNumber">
<property name="max" value="5"/>
<property name="tokens" value="METHOD_DEF"/>
<message key="maxParam" value="方法的参数不能超过5个,请使用对象包装。"/>
</module>

<!-- 方法长度 -->
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF"/>
<property name="max" value="60"/>
<message key="maxLen.method" value="方法做的事情太多,请拆分成多个方法。"/>
</module>

<!-- 单行代码长度 -->
<module name="LineLength">
<property name="max" value="120"/>
<message key="maxLineLen" value="单行代码过长,请换行。"/>
</module>


<!-- 代码设计检查,参考:http://checkstyle.sourceforge.net/config_coding.html -->
<!-- swtich 块中 default 的位置 -->
<module name="DefaultComesLast">
<message key="default.comes.last" value="default 应为 switch 块最后一个元素。"/>
</module>

<!-- swtich 块缺少 default -->
<module name="MissingSwitchDefault">
<message key="missing.switch.default" value="switch 块未定义 default 。"/>
</module>

<!-- 空语句 -->
<module name="EmptyStatement">
<message key="empty.statement" value="请移除空语句。"/>
</module>

<!-- equals 避免 null -->
<module name="EqualsAvoidNull">
<message key="equals.avoid.null" value="为防止空指针异常,字符串常量应出现在 equals 比较的左侧。"/>
<message key="equalsIgnoreCase.avoid.null" value="为防止空指针异常,字符串常量应出现在 equals 比较的左侧。"/>
</module>

<!-- equals 和 hasCode -->
<module name="EqualsHashCode">
<message key="equals.noEquals" value="重载 ''hashCode()'' 方法后,必须重载 ''equals()'' 方法。"/>
<message key="equals.noHashCode" value="重载 ''equals()'' 方法后,必须重载 ''hashCode()'' 方法。"/>
</module>

<!-- 字符串比较 -->
<module name="StringLiteralEquality">
<message key="string.literal.equality" value="字符串应使用 ''equals()'' 方法进行比较,而非 ''{0}''。"/>
</module>

<!-- 魔法数字 -->
<module name="MagicNumber">
<message key="magic.number" value="请将 ''{0}'' 替换成常量。"/>
</module>

<!-- 变量声明 -->
<module name="OneStatementPerLine">
<message key="multiple.statements.line" value="每行只能声明一个变量。"/>
</module>

<!-- 变量定义 -->
<module name="MultipleVariableDeclarations">
<message key="multiple.variable.declarations" value="每行只能定义一个变量。"/>
</module>

<!-- for 嵌套层级 -->
<module name="NestedForDepth">
<!-- value 下标从0开始 -->
<property name="max" value="2"/>
<message key="nested.for.depth" value="至多 3 层 for 嵌套。"/>
</module>

<!-- if 嵌套层级 -->
<module name="NestedIfDepth">
<!-- value 下标从0开始 -->
<property name="max" value="2"/>
<message key="nested.if.depth" value="至多 3 层 if 嵌套。"/>
</module>

<!-- try 嵌套层级 -->
<module name="NestedTryDepth">
<!-- value 下标从0开始 -->
<property name="max" value="2"/>
<message key="nested.try.depth" value="至多 3 层 try 嵌套。"/>
</module>

<!-- 简化布尔型的返回值 -->
<module name="SimplifyBooleanReturn">
<message key="simplify.boolReturn" value="不必要的条件逻辑。"/>
</module>

<!-- regexp 检查,参考:http://checkstyle.sourceforge.net/config_regexp.html -->
<!-- 禁止 java.lang.System 打印日志 -->
<module name="Regexp">
<property name="format" value="System\.(out|err)\.print*"/>
<property name="illegalPattern" value="true"/>
<message key="illegal.regexp" value="请使用 Log 打印日志。"/>
</module>
</module>
</module>

搭配 Android Studio

由于团队默认使用 Android Studio 开发,自然是将 CheckStyle 跟 Android Studio 结合起来了。我们想达到的效果是能实时检查不规范的代码并给出纠正的提示语,这是没问题的。下面是具体配置流程。

下载 CheckStyle 插件

在 Android Studio 的 Plugins 中下载最新版本的 CheckStyle-IDEA。

添加 CheckStyle

在项目根目录下添加 checkstyle.xml 文件。

关联 CheckStyle

checkstyle-config1
checkstyle-config2
checkstyle-config3
checkstyle-config7

CheckStyle 实时检查

checkstyle-config4
checkstyle-config5

CheckStyle 手动检查

checkstyle-config6
checkstyle-config8

OLDER > < NEWER