您还未登录! 登录 | 注册 | 帮助  

您的位置: 首页 > 软件开发专栏 > 开发技术 > 正文

利用IDEA代码审查能力,来保证代码质量

发表于:2021-08-12 作者:YourBatman 来源:BAT的乌托邦

前言

你好,我是方同学(YourBatman)

上篇文章【方同学】是如何高效的使用IntelliJ IDEA 再一次勾起了较多读者对IDEA的兴趣,让我没想到的是一个小小的IDE开发工具而已,甚至都与Java语言没有直接关系,竟然也能写成一个系列。也许它代表着一种态度,一种程序员对工具使用的态度,not only code。图片认识我的朋友可能比较清楚,笔者近些年一直从事和带领团队从事基础架构、中间件的研发工作。众所周知,写基建代码与业务开发有不同,它对代码质量、可靠性、性能、软件长久生命力均会有更高要求,毕竟基础不牢,地动山摇是共识。

本文就为你分享,笔者是如何借助IDEA自带的一些能力,对书写的代码进行(质量)审查的。工具的出生就为提效,熟练使用对个人甚至团队都有好处。

所属专栏

BATutopia-IntelliJ IDEA

相关下载

【女娲Knife-Initializr工程】访问地址:https://start.yourbatman.cn 或 http://152.136.106.14:8761

Java开发软件包(Mac):https://wangpan.yourbatman.cn/s/rEH0 提取码:javakit

程序员专用网盘上线啦,开放注册送1G超小容量,帮你实践做减法:https://wangpan.yourbatman.cn

版本约定

IntelliJ IDEA 2021.2

正文

提高代码质量的手段

代码作为软件的载体,是软件最为重要的组成部分。所以一个软件的质量如何,很大程度上是由代码质量决定的。也许在写几句hello world的时候软件质量尚可,但随着时间的推移、代码量的增加、需求的多变、团队协作日益复杂等因素的加入,往往在质量这条路上渐行渐远。蓦然回首,是不是感觉自己已忘初心,但依旧砥砺前行呢?

殊不知,代码并非编译通过,万事大吉,编译只是最最最最基础的保证而已。

大部分程序员是期望写出高质量的代码,对自己的代码质量要求是较高追求的。可一旦遇到赶工压力,尤其是在 deadline 之前,就很可能会把完成度很低的代码交出去,心想“反正有人给我检查,到时候再说吧”。但是,这些代码就好比是一台“行走的Bug制造机”,后患无穷。作为你的领导看到这样的代码上线,可谓慌得一批。

既然赶工期、deadline这种“事件”无法避免,并且团队内程序员的水平/追求也高低不一,怎么办?随着软件行业的发展,出现了一批又一批的方法论、手段、工具用于整体提升软件质量,下面按照离程序员由近及远的顺序从两个方面简要了解一下。

程序员侧 - 最行之有效

代码是由程序员写的,最了解它的莫过于程序员自己。因此在本侧若能够做好质量把控关,是最行之有效的。正所谓在离“用户”最近的地方发现问题、解决问题往往效率最高的。

当然,从品控的方式方法上,自然也有成套的解决方案。处在程序员侧,有个非常大的优势:可借助IDE提供的“超强”能力,高效的进行代码审查工作。

IDE通用代码审查

什么叫通用代码审查?说白了就是关乎代码格式、方法/变量命名、基础语法合理性等等,一般的IDE都有这样的能力。

比如本文接下来将要讲的IDEA代码审查能力,就是提供的这方面的能力。

静态代码检测

借助checkstyle、p3c这类工具,对代码进行静态检测,能够提早发现很多运行期潜在的bug/风险点。静态代码检测对Java这种静态语言效果极佳,这也是静态语言的巨大优势之一:健壮性强。它对动态语言(如Python、PHP等)有点无能为力,效果欠佳。

拿Java举例,像Long.equals(Integer)这种无数人踩过的坑,通过静态代码检测就可以规避。另外,代码规范、格式等等都可以通过静态代码检测来实现很好的约束。值得注意的是,别看只是格式,这也很重要,很多时候程序出现bug,代码格式才是原罪。对于一个普通的程序员来讲,第一任务是要写出人能看得懂的代码,其次才是机器。

单元测试

单元测试(UT),是指对软件中的最小可测试单元进行检查和验证。那什么叫最小可测单元呢?以Java语言为例,最小可测单元就是一个方法/函数。

写出一个可单测的代码其实是非常困难的,有工作经验的程序员或多或少经历过“拒绝写单元测试”的情况,根据我的经验,出现这种畏难情绪的根本原因是:不会写,没有可“抄”的,毕竟ctrl c + ctrl v才是第一生产力嘛,让自己去搞,没有的事。

国内的开发环境尚处在初、中期,普遍对单元测试的重视度不够。主要原因我认为有两点:

写好UT需要花费一定时间,而国内行情一般“工期紧”,较少做中长期规划

业务逻辑代码分层不够、耦合严重,导致写UT代码时工作量剧增,近乎无法单测

久而久之,积重难返。等到最后上级领导说要关注代码质量要求写单元测试的时候,受到的阻碍情绪将会是空前的,然后就陷入了恶性循环。

关于UT,开发者公认的事实:UT是确保代码健壮性极其有效的手段。根据现实情况,我本人对UT的态度是:尽量不要自顶向下的强推,而应疏。

单元测试其实是一门严重被低估的“学问”,经常是领导大嘴一巴说要写,至于怎么写可能领导自己也不知道,甚至可能从来没写过。正所谓己所不欲勿施于人,我认为UT覆盖率问题应该上升到方案级别,而不只是只有一个字:写。

畏难情绪是顺人性的,是天性的表现。在代码的世界了,不会才觉得难,才会抗拒。毕竟写UT远没有写业务代码来得那么的“轻松”。针对此问题,可采用榜样的力量逐步疏通、渗透。对于个人来讲,应该迎难而上,追求更高质量的代码。

CI/CD侧 - 统一卡点

虽然说在程序员侧进行一些质量把控环节效果是最佳的,但此方式高度依赖程序员本身的综合水平和自觉性,可靠性是明显不够的。因此在实际生产上,需要辅助一些集中式卡点行为来做保障。

Code Review

对每个提测的分支,(至少在上线前)必须要进行CR(code review)环节。此环节比较出名的工具有:Gitlab(商业版)、Fisheye等

集中式静态代码检测

简而言之,就是将你本地静态代码监测的能力搬到云端,进行集中检测。一般可分为全量检测和增量检测。此环节比较出名的工具有:sonar、cubase等

集成测试

对多个系统进行集成测试、边界测试。该环节一般属于QA人工介入阶段,和业务逻辑强关联,是最为耗时的阶段之一。

持续集成流水线工具

在软件发布阶段,也会有一些相应的审核、卡点机制。此环节比较出名的工具有:jenkins、hudson等

借助IDEA审查你的代码

做个小调查:有多少同学从来没有使用过甚至从来没有看过IDEA的code菜单呢?

不管你看没看过,应该直接或间接的使用过其相关功能。比如这个Generate...

自动为JavaBean生成get/set方法、构造器、hashCode()、toString()等方法。也许你会说现在用lombok基本不使用它了,对,这是事实。但是,再不济,像Override Methods、Implement Methods这些功能都用过吧,毕竟我不信方法复写、方法实现这种模板代码你会一字一句手敲~

那么,接下来重点向你介绍code菜单里面的代码审查功能,顺带也分享笔者是如何利用IDEA提供的这些能力去帮助自己、甚至帮助团队提高代码质量、保证代码质量的。

代码审查相关功能在Code菜单这里(如下图)。两条灰色的线条将这几项归为一类:

以这段示例代码为例审查:


Inspect Code...审查你的代码

Inspect:检查、审查。

点击OK运行,在problem视窗里可以看到“有问题”的代码:

经过审查,本工程里的“坏味道”代码就在problem视窗指出了。如图,IDEA针对性的提供了便捷的一键处理按钮,你可以快速处理,非常智能化和人性化有木有。

到这,你可能不禁想问:IDEA怎么知道这是坏味道代码的?遵照什么规则?显然,一切都是“有法可依”,它在这:设置 -> Editor -> Inspections

一般来讲,Inspections保持默认即可。但我会把拼写检查(毕竟英语并非咱母语,偶尔单词拼写错误甚至用拼音是可以原谅的,关掉它以节约点性能开销嘛)相关的关掉:Spelling、Typo

注意:Inspect Code只会帮你把坏味道代码“揪出来”,改还是不改的决策权还在于你自己。

Code Cleanup...精炼你的代码

不同于Inspect Code帮你指出坏味道的代码,它比较“狠”,会拿你的代码直接开刀。如下:

执行该动作不需要同意,IDEA会直接动手修改你的代码。

当然喽,你duck不必担心它改乱了:它有且只做同等语义的替换,使得代码组织起来更优雅,绝不会影响到程序的正确执行。

Tips:有时候人容易理解和优雅写法是相冲的,这个时候你就得三思是否有必要执行此功能喽

Analyze Code...分析你的代码

它是一个功能集的统称。

问:为何这些功能被放在二级目录下?答:相对来说不太常用。这是基本的产品设计逻辑嘛:常用的放在一级目录,不常用的功能下层

Silent Code Cleanup

静悄悄的完成精炼你的代码操作,执行结果同Code Cleanup。我的使用情况:基本不用

Run Inspection by name

如果觉得每次运行Inspect Code把所有的规则都走一遍太慢了,那么就可以使用此功能:只运行指定名称的规则

我的使用情况:基本不用。现在微服务开发模式,每个应用“体积”都很小,1s和0.5s的差异who care呢

View Offline Inspection Results

离线查看代码分析的结果。此功能的存在,是因为problem结果是可以导出的:

我的使用情况:只使用过几次。那是我要给团队做分享,所以通过导出文件来保留“现场”,从而在分享时在导入方便说明问题。

Infer Nullify

Infer:推断。此功能作用是在方法参数、返回值里帮你推断:哪些必须不能为null、哪些可以为null

以这个方法为例:


内部调用了s的toString()方法,因此s肯定不能为null,进而推断出返回值也就不可能为null。因此,运行IDEA的此功能后,代码会被改成这样:方法签名的语义更加明确


值得注意的是,注解它只用于表达语义,运行期无任何作用。另外,该功能要正常执行需要此依赖包才行:

若没有此依赖,执行时会弹出提示你添加依赖:

此时点击ok就能自动帮你把依赖加上了,非常方便。

Tips:点ok后此框不会消失,但实际依赖已经加上,不用重复点哦,否则会重复添加依赖的。这是IDEA的一个小bug

我的使用情况:从未使用过。因为我习惯使用功能更强大的Bean Validation,不仅语义明确,runtime时期也会生效。

Locate Duplicate

顾名思义,帮你定位重复代码,以便做封装、抽象。

我的使用情况:在review组员代码的时候,用得较多。查找结果还是蛮有参考价值的,推荐使用

Dependencies...

IDEA在Project视窗,把依赖统一全部放在了External Libraries里,就像这样:

若是一个多模块的项目,此时只想查看某1个模块的依赖的话,通过External Libraries就无能为力了。这个时候一般有两种办法来查看具体某个模块的依赖:

通过Maven视窗查看

这种方式,它对于中小型服务可以解决绝大部分问题,因为依赖不多不难定位。但是它的明显缺点是:不够直观。并不能一眼看出来模块的最终依赖。比如某个依赖是间接依赖进来的,这时通过这种方式就非常不直观了;又比如多个地方引入了a.jar,也就无法一眼看出来最终使用的是哪个版本的a.jar啦

通过本处的Dependencies功能

这个依赖分析功能非常、非常、非常强大。它不仅仅能帮你分析出每个类(粒度非常细)依赖哪些库,还能分析出依赖了本project的哪些类。该功能在阅读他人代码(or开源代码)时非常有用

我的使用情况:较为频繁。我在阅读开源代码的时候使用较多,通过依赖分析,能够较快的掌握作者的整体设计意图,具有更全面的视野。

Backward Dependencies...

上面功能是查看自己依赖了谁,该功能表示谁依赖了自己。

Module、Cycle Dependencies...

用于分析本工程的模块粒度的依赖关系,是否存在循环依赖等,使用起来比较简单,不详细解释了。

Analyze Stack Trace or Thread Dump...

又一神器,这个工具我可太爱了。字面含义:分析堆栈,或者内存dump。

例如有这么一个场景:线上服务出bug抛了异常,日志文件里留下的是堆栈信息,面对这个堆栈信息你如何快速定位到问题代码呢?

这个时候一般是在这个黑框里逐行的找,找到“熟悉”的一行(有行号),然后返回到IDEA里找到对应的类,问题定位路径其实还蛮长的。

其实duck不必这么麻烦,IDEA为咱们提供了非常好用的分析工具,你只需要:

  • 复制堆栈信息
  • 在IDEA里Code -> Analyze Stack Trace or Thread Dump打开分析窗口

点击ok,控制台里就能显示出堆栈信息,和本地调试一模一样的效果了有木有

这里我演示的是该功能的最简单使用场景,除了分析这种简单的堆栈外,还可以分析dump文件,还可以自定义分析器(比如class文件混淆、加密了)等等,最终目的就是让开发者有种本地化“分析线上问题”的体验。

关于本功能的更多使用方式,感兴趣的同学在真正用起来的时,可自己发现。

总结

本文以IDEA作为着力点,我主要想输出的两个观点是:

  • 单元测试被低估了
  • IDEA的代码审查能力被低估了

作为一个开发者,很多时候人与人的差异体现在知道的多与少、知道和不知道上,毕竟大家的教育背景、智商大都差异不大,会得多一点并且让这些能支撑自己总是好的。