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

2008-02-23 09:15:43来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

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

圈什么?

关于这篇文章和代码质量主题的任何其他文章的问题,请访问由 Andrew Glover 主持的 Improve your Java Code Quality 讨论论坛。

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

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

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

或者

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

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

代码度量的历史

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

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

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

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

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

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

未经雕琢的钻石

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

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

数字的海洋

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

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


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



if (entityImplVO != null) {

  List actions = entityImplVO.getEntitIEs();

  if (actions == null) {

     actions = new ArrayList();

  }

  Iterator enItr = actions.iterator();

  while (enItr.hasNext()) {

    entityResultValueObject arVO = (entityResultValueObject) actionItr

     .next();

    Float entityResult = arVO.getActionResultID();

    if (assocPersonEventList.contains(actionResult)) {

      assocPersonFlag = true;

    }

    if (arVL.getByName(

      AppConstants.ENTITY_RESULT_DENIAL_OF_SERVICE)

         .getID().equals(entityResult)) {

      if (actionBasisId.equals(actionImplVO.getActionBasisID())) {

        assocFlag = true;

      }

    }

    if (arVL.getByName(

     AppConstants.ENTITY_RESULT_INVOL_SERVICE)

      .getID().equals(entityResult)) {

     if (!reasonId.equals(arVO.getStatusReasonID())) {

       assocFlag = true;

     }

   }

 }

}else{

  entityImplVO = oldEntityImplVO;

}




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

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:如何在 Oracle JDeveloper 中进行 Ajax 编程

下一篇:面向方面的编程:它的好处是什么?