代码规范最佳实践
2016-10-03 / modified at 2022-05-01 / 1.8k words / 6 mins
️This article has been over 2 years since the last update.

代码规范是一个老生常谈的问题,涉及到一个码农的软技能。网上有很多类似的文章,但是有些文章比较落后了,因此需要补充新鲜的文章

主要参考如下

  1. 《编写可读代码的艺术》
  2. 《编程的智慧》
  3. 个人平时积累
  4. 菊花厂(网上自己搜)与月饼厂的编程规范

我个人对代码规范的要求,主要就是一点:避免自己几个月后看着代码愤而打开提交记录看谁写的渣代码

下面的内容还是比较新的,从逻辑判断、注释规范等方面进行了思考。

1. 代码编写

1.1. 关于if的使用场景

1.1.1. 不要省略if中的其它条件

if是很容易犯错的地方,因此不要省略if中的其它条件,就算它什么也不做也要写出来,否则以后将是一个定时炸弹,编译器会把你的括号给自动消除的

1
2
3
4
5
6
7
8
9
if(a < 2){
if(b > 3){
//do sth.
} else{
//do nothing
}
} else{
//do nothing
}
1.1.2. 不要使用反义词

在使用boolean时,不要使用反义词,减少大脑Parser时间

1
2
3
4
5
6
7
8
9
10
11
//bad
boolean isNotExistInTable;
if(!isNotExistInTable){
//到底判断条件是啥?负负得正?
}

//good
boolean isEmptyInTable;
if(!isEmptyInTable){
//明白了,这里是Table中元素不为空的场景
}
1.1.3. 尤达表示法

尤达表示法曾经在C中非常流行,不过它的时代已经过去了

1
2
3
4
5
6
7
8
9
//bad, 多此一举
if(null == str){

}

//good, 因为编译器或者IDE均会进行校验,写成`str=null`会被检查出来了
if(str == null){

}

在某些C硬件开发中,编译器无法监测此问题,这样写情有可原。但是在Java中没有必要照搬过来。

1.1.4. 按照“电路”逻辑进行编写

不要把判断全部写到if括号里

1
2
3
4
//bad
if(!(age > 18)&&!(today - startday > 30)){
//wtf,大脑分析到一半忘记前面的变量了
}

下面就是按照“电路”逻辑进行编写的,每行一个比较,就算使用了b1,b2这样的命名,也能够看得懂,同时打断点也很舒服

1
2
3
4
5
6
7
8
//good
//必须18以上才能购买
boolean b1 = age > 18;
//如果小于等于30天,那么将没有优惠
boolean b2 = today - startday > 30;
if(!(b1&&b2)){
//
}

1.1.5. 复杂业务不要使用Lambda表达式

虽然Java8提供了此方法,但是它与JS、Ruby、Groovy的风格完全不同。在Java中通过接口模拟实现,而在动态语言中通过block或者高阶函数实现,信息量也完全不同,除非全部成员都是极其熟悉被lambda隐藏的操作符。

不信的话,你写完代码后一个星期后看看是否还看得懂吧。

1.2. 关于流程处理

流程在业务代码中广泛出现,建议除了面试,不要在业务中使用goto、doWhile、continue、break,甚至for/while/iterator代码,它们可以被如下替换

  • try/finally
  • 函数式编程: Java8stream, RxJava, Guava, CommonCollection4, 特别推荐stream,它内部与Haskell一样支持惰性求值,效率肯定比自己写的轮子更优秀。

我个人推荐使用函数式表达式,也就是类似于脚本语言的写法来写Java,看黑板:

idea_2016_3_streams_intentions_7.gif

1.3. 注释中加入自己的判断与想法

1.3.1. 使用大家都认可的行话

比如FIXME,TODO等,这个在Intellij中有快捷键

1
2
3
4
//FIXME: 在某场景下调用时出现一个异常没有复现,详见xxx工单
private doWork(){

}
1.3.2. 警告他人调用的陷阱
1
2
3
4
//警告,内部使用了for循环查表,可能导致阻塞而变慢
public list<String> querybyId(List<Node> nodes,long id){

}
1.3.3. 注释中描述修改的原因
1
2
3
4
5
6
private doWork(){
//根据工单xxx进行修改,处理了某场景问题
sth.doAnother();
//虽然看似没用,这样写的原因是....
sth2.doAnother();
}

无论何时,只要是你心里所想,只要不是用来粉饰烂代码,就可以写出来,以后代码更新时,可以快速进入状态,避免再次读码而浪费时间

1.3.4. 对重要的对象提供一个例子

在构造变量时,可以通过注释例子帮助理解。不要吝啬代码在VCS中所占用的空间,以免后期查阅文档浪费更多的时间

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
/**
* the id of employee, [a-zA-Z0-9/s]{0,12}
* eg: xyz223344
**/
String id;

/**
* the blur redis, must be power of 2
* eg: 2, 4, 8
**/
int BLUR_RADIS = 8;

/**
* return the person whoes age > ${age}
* eg: (18,[14,46,18,50]) -> [46,50]
**/
private List<Person> filterByAge(int age, List<Person> person){

}

/**
* valid by regex defined in file: xxx.ccc.bbb.xml
**/
private boolean doVaild(String s){

}

1.4. 使用注解

添加注解,让你的IDE更加智能地进行静态检测

  1. Android: Improve Code Inspection with Annotations,注意可以将注解设置为provided模式
  2. Java: 使用JavaX自带包,注意这个包里面老是在变,我个人日常也是只用Null判断

1.5. 建立自己的Util

除了Apache等出名的工具类外,也可以将自己的通用代码放入Utils中,好处如下

  1. 降低代码量
  2. 对Apache等工具类不完善的地方进行再包装
  3. 写完了可以骗Star(当然我个人不赞同,也不敢用)
  4. 熟悉常用的库,减少自己造轮子

2. 编码完成后,使用CodeFormatter

代码格式化(CodeFormatter)工具涉及到审美、对齐、缩进、左括号之争。统一的格式能减少代码阅读时的误判。

我个人推荐用Square/默认的格式,倒入格式后cmd +alt + L即可

1
https://github.com/square/java-code-styles

如果你所在公司有自己的格式要求,就算你认为“不合理”,也要按照公司大流来

3. 使用静态分析工具检查源码

静态分析工具通过对Java文件的AST进行分析,找出潜在的问题,通过自动化工具节约时间。

1. Intellij

使用Intellij的代码检查功能,平时尽量清零掉IDE右边的黄色警告(比如未使用的变量、错误的拼写),写完代码后,尽量用Inspect去跑一遍整个代码

2. FindBugs/PMD/SonarQube

这个同上,许多公司都用它作为VCS门禁,没跑通不能上库。在本地环境它可以作为Intellij的插件引入,它是根据class文件进行静态分析的,功能如其名。建议将设置中的报警级别调为最敏感。

4. SUM

总的来说,代码还是需要积累,建议多看源码,不管是我的文章,还是教你写代码的书籍,还是厚厚的编码规范,都不如直接看现成的代码,各位可以mark下面几个项目,纯函数,没有复杂逻辑