每位开发人员对代码质量的含义都有着自己的看法,并且大多数人对如何查找编写欠佳的代码也有自己的想法。甚至术语代码味道(code smell) 也已进入大众词汇表,成为描述代码需要改进的一种方式。

代码味道通常由开发人员直接判定,有趣的是,它是许多代码注释综合在一起的味道。一些人声称公正的代码注释是好事情,而另一些人声称代码注释只是解释过于复杂的代码的一种机制。显然,Javadocs™ 很有用,但是多少内嵌注释才足以维护代码?如果代码已经编写得足够好,它还需要解释自己吗?

这告诉我们,代码味道是一种评估代码的机制,它具有主观性。我相信,那些闻起来味道糟透了的代码可能是其他人曾经编写的最好的代码。以下这些短语听起来是不是很熟悉?

是的,它初看起来有点乱,但是您要看到它多么可扩展!!

或者

它让您感到迷惑,但显然您不了解它的模式。

我们需要的是客观评估代码质量的方法,某种可以决定性地告诉我们正在查看的代码是否存在风险的东西。不管您是否相信,这种东西确实存在!用来客观评估代码质量的机制已经出现了一段时间了,只是大多数开发人员忽略了它们。这些机制被称为代码度量 (code metric)。

代码度量的历史

几十年前,少数几个非常聪明的人开始研究代码,希望定义一个能够与缺陷关联的测量系统。这是一个非常有趣的主张:通过研究带 bug 代码中的模式,他们希望创建正式的模型,然后可以评估这些模型,在缺陷成为缺陷之前 捕获它们。

在这条研究之路上,其他一些非常聪明的人也决定通过研究代码看看他们是否可以测量开发人员的生产效率。对每位开发人员的代码行的经典度量似乎只停留在表面上:

Joe 生产的代码要比 Bill 多,因此 Joe 生产率更高一些,值得我们花钱聘请这样的人。此外,我注意到 Bill 经常在饮水机边闲晃,我认为我们应该解雇 Bill。

但是这种生产率度量在实践中是非常令人失望的,主要是因为它容易被滥用。一些代码测量包括内嵌注释,并且这种度量实际上受益于剪切粘贴式开发 (cut-and-paste style development)。

Joe 编写了许多缺陷!其他每条缺陷也都是由他间接造成的。我们不该解雇 Bill,他的代码实际上是免检的。

可以预见,生产率研究被证实是非常不准确的,但在管理团队 (management body) 广泛使用这种生产率度量以期了解每个人的能力的价值之前,情况并非如此。来自开发人员社区的痛苦反应是有理由的,对于一些人而言,那种痛苦感觉从未真正走远。

未经雕琢的钻石

尽管存在这些失败,但在那些复杂度与缺陷的相互关系的研究中仍然有一些美玉。大多数开发人员忘记进行代码质量研究已有很长一段时间了,但对于那些仍正在钻研的人而言(特别是如果您也正在为追求代码质量而努力钻研),会在今天的应用中发现这些研究的价值。例如,您曾注意到一些长的方法有时难以理解吗?是否曾无法理解嵌套很深的条件从句中的逻辑?您的避开这类代码的本能是正确的。一些长的方法和带有大量路径的方法 难以理解的,有趣的是,这类方法容易导致缺陷。

我将使用一些例子展示我要表达的意思。

数字的海洋

研究显示,平均每人在其大脑中大约能够处理 7(±2)位数字。这就是为什么大多数人可以很容易地记住电话号码,但却很难记住大于 7 位数字的信用卡号码、发射次序和其他数字序列的原因。

此原理还可以应用于代码的理解上。您以前大概已经看到过类似清单 1 中所示的代码片段:

清单 1. 适用记忆数字的原理

清单 1 展示了 9 条不同的路径。该代码片段实际上是一个 350 多行的方法的一部分,该方法展示了 41 条不同的路径。设想一下,如果您被分配一项任务,要修改此方法以添加一项新功能。如果您该方法不是您编写的,您认为您能只做必要的更改而不会引入任何缺陷吗?

当然,您应该编写一个测试用例,但您会认为该测试用例能将您的特定更改在条件从句的海洋中隔离起来吗?

测量路径复杂度

圈复杂度 是在我前面提到的那些研究期间开创的,它可以精确地测量路径复杂度。通过利用某一方法路由不同的路径,这一基于整数的度量可适当地描述方法复杂度。实际上,过去几年的各种研究已经确定:圈复杂度(或 CC)大于 10 的方法存在很大的出错风险。因为 CC 通过某一方法来表示路径,这是用来确定某一方法到达 100% 的覆盖率将需要多少测试用例的一个好方法。例如,以下代码(您可能记得本系列的第一篇文章中使用过它)包含一个逻辑缺陷:

清单 2. PathCoverage 有一个缺陷!

作为响应,我可以编写一个测试,它将达到 100% 的行覆盖率:

清单 3. 一个测试产生完全覆盖!

接下来,我将运行一个代码覆盖率工具,比如 Cobertura,并将获得如图 1 中所示的报告:

图 1. Cobertura 报告

哦,有点失望。代码覆盖率报告指示 100% 的覆盖率,但我们知道这是一个误导。

二对二

注意,清单 2 中的 pathExample() 方法有一个值为 2 的 CC(一个用于默认路径,一个用于 if 路径)。使用 CC 作为更精确的覆盖率测量尺度意味着第二个测试用例是必需的。在这里,它将是不进入 if 条件语句而采用的路径,如清单 4 中的 testPathExampleFalse() 方法所示:

清单 4. 沿着较少采用的路径向下

正如您可以看到的,运行这个新测试用例会产生一个令人讨厌的 NullPointerException。在这里,有趣的是我们可以使用圈复杂度而不是 使用代码覆盖率来找出这个缺陷。代码覆盖率指示我们已经在一个测试用例之后完成了此操作,但 CC 却会强迫我们编写额外的测试用例。不算太坏,是吧?

幸运的是,这里的测试中的方法有一个值为 2 的 CC。设想一下该缺陷被隐藏在 CC 为 102 的方法中的情况。祝您好运找到它!

图表上的 CC

Java 开发人员可使用一些开放源码工具来报告圈复杂度。其中一个这样的工具是 JavaNCSS,它通过检查 Java 源文件来确定方法和类的长度。此外,此工具还收集代码库中每个方法的圈复杂度。通过利用 Ant 任务或 Maven 插件配置 JavaNCSS,可以生成一个列出以下内容的 XML 报告:

  • 每个包中的类、方法、非注释代码行和各种注释样式的总数。
  • 每个类中非注释代码行、方法、内部类和 Javadoc 注释的总数。
  • 代码库中每个方法的非注释代码行的总数和圈复杂度。

该工具附带了少量样式表,可以使用它们来生成总结数据的 HTML 报告。例如,图 2 阐述了 Maven 生成的报告:

图 2. Maven 生成的 JavaNCSS 报告

此报告中带有 Top 30 functions containing the most NCSS 标签的部分详细描述了代码库中最长的方法,顺便提一句,该方法几乎总是 与包含最大圈复杂度的方法相关联。例如,该报告列出了 DBInsertQueue 类的 updatePCensus() 方法,因为此方法的非注释行总数为 283,圈复杂度(标记为 CCN)为 114。

正如上面所演示的,圈复杂度是代码复杂度的一个好的指示器;此外,它还是用于开发人员测试的一个极好的衡量器。一个好的经验法则是创建数量与将被测试代码的圈复杂度值相等的测试用例。在图 2 中所见的 updatePCensus() 方法中,将需要 114 个测试用例来达到完全覆盖。

分而治之

在面对指示高圈复杂度值的报告时,第一个行动是检验所有相应测试的存在。如果存在一些测试,测试的数量是多少?除了极少数代码库以外,几乎所有代码库实际上都有 114 个测试用例用于 updatePCensus() 方法(实际上,为一个方法编写如此多的测试用例可能会花费很长时间)。但即使是很小的一点进步,它也是减少方法中存在缺陷风险的一个伟大开始。

如果没有任何相关的测试用例,显然需要测试该方法。您首先想到的可能是:到重构的时间了,但这样做将打破第一个重构规则,即将编写一个测试用例。先编写测试用例会降低重构中的风险。减少圈复杂度的最有效方式是隔离代码部分,将它们放入新的方法中。这会降低复杂度,使方法更容易管理(因此更容易测试)。当然,随后应该测试那些更小的方法。

在持续集成环境中,随时间变化 评估方法的复杂度是有可能的。如果是第一次运行报告,那么您可以监视方法的复杂度值或任何相关的成长度(growth)。如果在 CC 中看到一个成长度,那么您可以采取适当的动作。

如果某一方法的 CC 值在不断增长,那么您有两个响应选择:

  • 确保相关测试的健康情况仍然表现为减少风险。
  • 评估重构方法减少任何长期维护问题的可能性。

还要注意的是,JavaNCSS 不是惟一用于 Java 平台促进复杂度报告的工具。PMD 是另一个分析 Java 源文件的开源项目,它有一系列的规则,其中之一就是报告圈复杂度。CheckStyle 是另一个具有类似的圈复杂度规则的开放源码项目。PMD 和 CheckStyle 都有 Ant 任务和 Maven 插件

使用复杂度度量

因为圈复杂度是如此好的一个代码复杂度指示器,所以测试驱动的开发 (test-driven development) 和低 CC 值之间存在着紧密相关的联系。在编写测试时(注意,我没有暗示是第一次),开发人员通常倾向于编写不太复杂的代码,因为复杂的代码难以测试。如果您发现自己难以编写某一代码,那么这是一种警示,表示正在测试的代码可能很复杂。在这些情况下,TDD 的简短的 “代码、测试、代码、测试” 循环将导致重构,而这将继续驱使非复杂代码的开发。

所以,在使用遗留代码库的情况下,测量圈复杂度特别有价值。此外,它有助于分布式开发团队监视 CC 值,甚至对具有各种技术级别的大型团队也是如此。确定代码库中类方法的 CC 并连续监视这些值将使您的团队在复杂问题出现时 抢先处理它们。

代码质量度量标准_追求代码质量(2): 监视圈复杂度相关推荐

  1. 代码质量度量标准_Google研发度量改进实践

    Google改进过程: 本文案例源自:<Measuring Engineering Productivity> 作者:Ciera Jaspen,Google 前言 随着敏捷开发.DevOp ...

  2. java 圈复杂度_追求代码质量: 监视圈复杂度

    每位开发人员对代码质量的含义都有着自己的看法,并且大多数人对如何查找编写欠佳的代码也有自己的想法.甚至术语代码味道(code smell) 也已进入大众词汇表,成为描述代码需要改进的一种方式. 圈什么 ...

  3. 追求代码质量: 监视圈复杂度

    http://www.ibm.com/developerworks/cn/java/j-cq03316/ 每位开发人员对代码质量的含义都有着自己的看法,并且大多数人对如何查找编写欠佳的代码也有自己的想 ...

  4. 表示python代码块的是_编写高质量Python代码的59个有效方法,你用过几个

    欢迎点击右上角关注小编,除了分享技术文章之外还有很多福利,私信学习资料可以领取包括不限于Python实战演练.PDF电子文档.面试集锦.学习资料等. 这个周末断断续续的阅读完了<Effectiv ...

  5. 代码管理 防止员工_低代码开发现形记

    新技术浪潮下的产业变革,使各种技术不断出现.消失或被沿用,多年来底层技术平台的发展,为互联网产品提供了重要驱动力,而在互联网下半场,技术能力正在从C端市场转向B端,对业务及IT架构的赋能将决定着一家企 ...

  6. dev c++代码自动补全_让代码自动补全的全套流程

    作者: 熊唯,黄飞 ,腾讯 PCG/QQ研发中心/CV应用研究组 AI 如果真的可以写代码了,程序员将何去何从?近几年,NLP 领域的生成式任务有明显的提升,那通过 AI 我们可以让代码自动完成后续补 ...

  7. 为了提高python代码运行速度和进行_一行代码让你的python运行速度提高100倍

    Python用的好,猪也能飞起来.今天,带大家学习如何让Python飞起来的方法,干货满满哦! python一直被病垢运行速度太慢,但是实际上python的执行效率并不慢,慢的是python用的解释器 ...

  8. 13款宝马x5质量到底怎么样_宝马x5质量到底怎么样 真实车主告诉你宝马X5质量如何...

    很多人都想知道宝马X5质量怎么样,这辆车作为一辆豪华中大型SUV,可以说无论是内饰还是配置都是同级别中的顶级水平,加上宝马一贯的操控性,可以说满足了很多人的驾驶需求,今天小编便请到一位真实车主来告诉你 ...

  9. java代码画樱花飘落_一行代码引入博客园樱花飘落特效

    前言 博客园作为面向大众的博客, 个性新颖可以博得一赞, 简约美观也不失阅读体验, 本文对樱花特效js进行了解读, 发现作者的设计确实秒不可言, 即使没有注释, 思路展示的也很清晰. 那就废话不多说, ...

最新文章

  1. linux 内核 netfilter 网络过滤模块 (2)-conntrack
  2. 会计期间变式OB52
  3. macos下载的安装包在哪里_macbook任意降级,为您带来mac os完美降级教程
  4. 2022年,图机器学习Graph ML发展到哪了?
  5. POJ1579 HDU1331 HDU1579 ZOJ1168 Function Run Fun【记忆化递归】
  6. Dubbo/ Spring Cloud 分布式事务管理 LCN方式
  7. CMU 15-213 Introduction to Computer Systems学习笔记(12) Linking
  8. 菜鸡程序员的一天都在折腾些什么?
  9. 【雷达通信】基于matlab粒子群算法优化综合微带天线阵列方向图【含Matlab源码 1967期】
  10. navicat mysql视图_Navicat教你如何做MySQL 视图
  11. 办公室计算机联机,两台电脑怎样联机?
  12. linux下登录ftp使用lftp命令详解
  13. Android进阶知识树——Android消息队列
  14. java连接SQL Server 2005数据库教程(手把手教程)
  15. JVM笔记:Java虚拟机的字节码指令详解
  16. iPhone 一键打开北京健康宝
  17. 图解Java数据结构之双向链表
  18. 压缩BCD码和非压缩BCD码的区别
  19. 挖矿病毒 qW3xT.2 最终解决方案
  20. 云服务器部署stable diffusion webui

热门文章

  1. MySQL修改my.cnf配置不生效的解决方法
  2. Nagios Plugin for Cacti (npc插件) Download 下载
  3. html 标签面板,HTML 标签大全及属性
  4. linux nginx django,如何在Linux下使用Nginx部署Django项目
  5. c语言实现1024点fft程序,C语言1024点快速傅里叶变换(FFT)程序,最好经过优化,执行速度快...
  6. 基于JAVA+SpringMVC+MYSQL的健身房管理系统
  7. HTML用于定义宽度的是,HTML Style columnRuleWidth用法及代码示例
  8. Spark 学习(十一) spark使用hive的元数据信息
  9. gentoo/funtoo 环境配置使用 valgrind
  10. Just do IT --- gulp