很早很早以前,只有Sun写于1999年的 《Java编码规范》 ,里面多是命名和格式的规定。后来万能的Google 也出了一份 ,但内容仍以这两者为主。直到最近 《阿里巴巴Java开发手册》 ,作为首个对外公布的企业级Java开发手册,里面整理的大量最佳实践,对整个业界都有大的意义。
受益于阿里手册开放的Apache 2.0 License,我们在其基础上结合唯品会的内部经验定制了一个自己的版本。
前人栽树,讲解一次规范本来是件很苦很累的事情,但有了阿里手册大量详尽的周边介绍文章,我们就可以偷个懒,只说说自己定制的部分好了。修改主要有三:
1. 《Clean Code》 、 《Effective Java》 ,佳篇难弃,一并补充。
2. 《SEI CERT Oracle Coding Standard for Java》 和 Sonar的 零零碎碎的规则 。
3. 规则尽量精简,期望所有规则都真正影响代码质量和可读性,让开发人员看到修改建议项都觉得是服气的,而不觉得是学院派在唠唠叨叨,浪费大家时间。
特别声明:全文纯技术表达,无意挑起任何争端,白前辈保佑不要有煽风点火的小人跳将出来。
一切规范,都得自动化的代码检查工具保证其落地。
一切工具,都不能自动化检查规范里的所有规则。
1. 最早是简单的checkstyle,后来有同样基于文本分析的PMD
2. 再后来又有了基于字节码分析的FindBugs
3. 再后来出现了Sonar,集成了三者
4. 又后来,Facebook基于Findbugs推出自己实战派的规则FB-Contrib,再次被Sonar集成。
5. 最后,Sonar发展了自己的语法分析体系,改进并替换了Checkstyle 和 PMD的规则。
阿里基于PMD写了自己的规则集和IDE 插件P3C。而我们则仍然选择Sonar,
一来,Sonar既有IDE Plugin,又有服务侧的DashBoard給所有的项目相关者共享,还可与其他系统打通质量数据。
二来,Sonar + FindBugs + FB Contrib 体系已包含了大量的现成规则。
三来,PMD的规则极度难读难改,只有阿里同学能玩得转。对于我们来说,Sonar的用Java编写的规则亲切得多。
阿里手册比较强硬的禁止了拼音与英文的混合。
“【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。”
而我们考虑到一些中国式业务词汇,可能缺乏一个比较通用好懂的英文对应,用金山词霸硬翻出生僻的英文,反而给阅读者制造了困难,所以拼音还是不能禁止。
既然允许了拼音,那混合似乎也不能禁止,比如例子里的getPingfenByName(),如果PingFen找不到英文对应,那总比GenJuMingZiHuoQuPingFen 好。
又新增了拼音缩写的禁止,对新来的阅读者这个是真的很难猜,所以规则重写成:
“禁止拼音缩写,避免阅读者费劲猜测;尽量不用拼音, 除非中国式业务词汇没有通用易懂的英文对应。”
来自《Clean Code》的精华浓缩:
1)如果上下文很清晰,局部变量可以使用 `list` 这种简略命名, 否则应该使用 `userList` 这种更清晰的命名。
2)禁止 `a1, a2, a3` 这种带编号的没诚意的命名方式。
3)方法的参数名叫 `bookList` ,方法里的局部变量名叫 `theBookList` 也是很没诚意。
4)如果一个应用里同时存在 `Account、AccountInfo、AccountData` 类,或者一个类里同时有 `getAccountInfo()、getAccountData()`, `save()、 store()` 的函数,阅读者将非常困惑。
5) `callerId` 与 `calleeId`, `mydearfriendswitha` 与 `mydearfriendswithb` 这种拼写极度接近,考验阅读者眼力的。
比如父类有个foo的成员变量,而Java允许子类再定义一个foo的成员变量。
同理,有了bar的成员变量,但它的函数参数,或者局部变量,仍然可以叫bar,能把人看晕了。
好在,Sonar有规则兜了底。
Sonar的规则很聪明,即使是static final的变量,如果不是基础类型如String,Int 就不当成是常量,也不会提醒你大写它,比如static final Logger logger。
比如一些不属于命名规范的规则移除了,看 定制记录 ,不一一啰嗦
所有规范中,关于空格,括号等等的描述是最无趣难读难记的,所以强烈建议用项目组统一的代码格式模板来代替这些描述。
而到底行宽是80,100 还是120字符,缩进是Tab或二空格 ,四空格不重要,大家统一就好。
这是《Clean Code》的一个重要思想,你要按一篇文章来布局你的代码,想象一个阅读者的存在。
方法要排列,而不是总把新增方法放在最后面。
1)顺序依次是:构造函数 >分组的 (公有方法/保护方法/私有方法) > getter/setter方法。
2)当一个类有多个构造方法,或者多个同名的重载方法,这些方法应该放置在一起。其中参数较多的方法在后面。
3)作为调用者的方法,尽量放在被调用的方法前面。
同理,一段代码也是一段文章,需要合理的分段,而不是一口气读到尾。不同组的变量之间,不同业务逻辑的代码行之间,有时候插入一个空行,起逻辑分段的作用,等于一句无言的注释。
我们没有理由假设读者能记住整个Java运算符优先级表。除非作者和Reviewer都认为去掉小括号也不会使代码被误解,甚至更易于阅读。
对于一些特殊场景(如使用大量的字符串拼接成一段文字,或者想把大量的枚举值排成一列),为了避免IDE自动格式化,可以使用@formatter: off和@formatter: on来包装这段代码。
其他看 定制记录 ,不一一啰嗦
有些规范过度强调了所有方法都必须有注释,而我们特别说明了如果代码命名与结构足够清晰,的确可以不写注释。
大爱《Clean Code》,也提倡删掉下面这些无意义的注释和标签,让代码更整洁。
/** * put elephant into fridge. * * @param elephant * @param fridge * @return */ public void put(Elephant elephant, Fridge fridge);
在那些老式规范中,提倡大家必须使用/</p/>, /</pre/>这样的html标签,以及/< ,/"这样的转义字符来格式化注释,这些标志只对生成HTML版的JavaDoc有用,而对我们直接阅读代码时造成了很大的阅读困扰。
鉴于实际场景里我们很少阅读Html的JavaDoc,多数是直接阅读代码,所以我们提倡不使用它们,并禁止IDE对JavaDoc的格式化。
阿里手册里: 【强制】所有的类都必须添加创建者和创建日期。
但代码后续还会有多人多次维护,创建人也可能会离职,所以让我们相信源码版本控制系统对更新记录的管理能做得比这更好吧。
看似不累,码完三章规范还是很累,后面的章节下回再续了。
规范还是初版,欢迎大家去github留言,一起改进。