天天看点

跟ApacheBeam学质量控制之道Enforcer插件

在学习、开发apache beam源码过程中,除了它精妙的设计(通过几个简单的概念抽象把实时和离线的计算逻辑模型统一了起来),庞大的代码量(java 33万行, python9万行),还有一个比较大的感受是它的质量控制做得特别好,比之前参与过的其它一些开源项目都要好,这可能跟google的工程质量高于业界有关。但是这里面也没什么什么奇技淫巧,只是善用了一些插件而已,我在这里想把我在apache beam里面看到、学到的一些质量实践分享给大家。代码质量提升其实没有什么太多的捷径,这里要分享的也不是多么高大上的道理,每个小点都是很零碎的一个小技巧,但是所有这些<code>小技巧</code>组合起来,会让你对代码的质量更有信心。

事物的好坏是对比出来的,我们先来看看我们代码里面一些问题。

比如:

再比如:

我们代码很多没有javadoc, 或者好一点的有javadoc,但是不符合javadoc规范(用javadoc实际去生成会报格式不对)。

下面截取一段cap里面的代码:

比如这里这行代码长达138,与一般的推荐配置(80, 最多100)长多了,已经超出一屏了,看起来很难受。

用依赖分析工具分析一下cap的代码依赖,可以发现,里面有很多没用的依赖以及用到了但是没有声明的依赖。

这些每个问题单个看起来都不是大问题,但是堆积起来会让代码看起来有点<code>脏</code>。

下面我介绍一下我在beam里面看到的质量控制相关的一些小技巧,主要是一些maven插件的使用和一些测试库的使用,以及一些思想上的意识。

dependency插件的作用是帮我们扫描项目的依赖问题,它可以把我们实际要到了,但是没有声明的依赖;或者实际没用到,但是声明了的依赖都给找出来,帮你保持代码的纯洁, 下面是一个扫描报错的例子:

这里我们声明了一个没有用到的,但是声明了的<code>auto-value</code>的依赖,因此编译失败了。有了这个插件的保证,我们可以很有自信的知道我们引入的每个依赖都是有用的,有意义的。

checkstyle is a development tool to help programmers write java code that adheres to a coding standard. it automates the process of checking java code to spare humans of this boring (but important) task. this makes it ideal for projects that want to enforce a coding standard.

它能做的一些典型检查包括:

fallthrough: 如果你的switch/case里面没有写break,它会自动检测出来。

customimportorder: 检查import的顺序符合指定样式。

linelength: 检查代码行数不要超长。

methodlength: 检查方法行数不要超长。

下面是我最近在写beam代码的时候编译时候报的几个checkstyle错误:

enforcer是另一比有意思的maven插件, 它可以帮你指定你代码所需的运行环境,比如需要的maven版本(maven不同版本之间有时候行为差异还是很大的,比如我们采云间的代码基本都是用3.x版本编译的,但是开发机上默认装的是2.x的,这样编译的时候就会报很奇怪的错误,而enforcer插件则可以把这个需求明确化,如果你的maven的版本不对,它会明确告诉你maven版本不对,而不是其它的诡异错误)。它能做到的一些检查包括:

maven的版本

jdk的版本

os的版本

检查指定的属性(property)是否存在

在我看来maven-enforcer-plugin有点像java语言里面assert,assert如果失败了,说明运行环境有问题,直接失败。

下面是一个maven版本不对的错误:

最近这些年函数式编程的思维开始流行起来,很多非函数式的语言也开始在语言层面支持某些函数式的特征,比如java 8里面的<code>lambda</code>, <code>stream</code>等等,google autovalue也是类似的目的,它的目的是让你的pojo编程readonly的(只有getter), 从而实现函数式语言里面immutable的特性。

它最大的贡献在于,它让你只需要定义你需要的字段:

这里我们定义了我们需要两个字段: <code>name</code>和<code>numberoflegs</code>以及一个用来构建<code>animal</code>对象的<code>create</code>方法,其它的则都由autovalue自动生成,其中<code>autovalue_animal</code>就是autovalue自动生成的类。在autovalue_animal里面,它帮我们自动实现了<code>hashcode</code>, <code>equals</code>, <code>tostring</code>等等这些重要的方法。这些方法的特征是实现的过程基本都一样,但是容易出错(由于粗心), 那么不如交给框架去自动产生。

如果你的类字段比较多,那么autovalue还支持builder模式:

这样我们就可以一步一步渐进地把对象构造出来了。

所谓的api surface是指我们一个系统暴露给另外一个系统一个sdk的时候,我们到底应该把哪些类,哪些package暴露给用户,这个问题很重要,因为考虑向后兼容性的话,暴露的package越少,将来修改的时候,破坏向后兼容性的可能性就越小,sdk就越稳定。我们平常的时候对于这种事情可能都是通过人肉分析、review,在beam里面它直接编写成了一个单元测试:

这个测试表明,beam的sdk向外暴露的package就是上面列出的这些,下次来个新手贡献的代码如果破坏了这个约定,那么这个单元测试就直接报错,无法提交merge。还是那句话,凡是能自动化检测的东西不要依靠人肉。这里涉及到的主要技术是:

通过扫描classpath对指定包里面所有类的方法,参数,返回值进行检查。

<code>hamcrest</code>这个支持<code>matcher</code>的单元测试的库,它让你不只可以<code>asserttrue</code>, <code>assertfalse</code>, <code>assertequals</code>, 而是可以自由指定需要满足的条件。

给beam贡献代码的时候最大感受在于,你不需要去问其它commiter或者看什么文档去确认你的代码风格是否符合,是否用对了maven版本,jdk的版本对不对,javadoc需不需要写等等,你只要编译下代码,maven会告诉你的代码是否ok。只要通过了编译,代码风格、一些小的错误等等基本不会有了,那么代码review的时候主要就集中在代码设计层面了。有用的maven插件还有很多,这里只是举了几个例子,我觉得比每个插件本身更重要的是:

对代码质量的重视的意识。

使用工具/代码(而不是文档、流程)来解决保障代码质量。