简介: 软件指标可以帮助您寻找代码中隐藏的设计元素,让它们能够成为惯用模式。 演化架构与紧急设计 的这一期讲解如何使用指标和可视化发现被复杂性掩盖的重要代码元素。
紧急设计的难题之一是寻找隐藏在代码中的惯用模式和其他设计元素。指标和可视化有助于识别代码的重要部分,从而提取出一些设计元素。本文主要讨论两个指标,圈复杂度(cyclomatic complexity) 和传入耦合(afferent coupling)。圈复杂度度量方法的相对复杂度。传入耦合表示有多少个其他类使用当前类。本文要介绍显示和理解这两个指标的一些工具,以及如何通过组合指标帮助发现设计特征。
我在 “测试驱动设计,第 2 部分” 中讨论过圈复杂度,但是有一些细节没有讨论。通过 Java 工具度量圈复杂度的一个难点是工作单元。圈复杂度是方法级度量,但是 Java 编程中的工作单元是类。因此,圈复杂度指标通常表示为类中所有方法的总复杂度或平均复杂度。这两种形式都是有意义的。
例如,可能会出现以下情况。假设一个类包含一个非常复杂的方法 (CC = 40),但是还有许多非常小的方法(比如 Java 代码中常见的 get/set 方法对)。JavaNCSS等工具报告所有方法的总复杂度,因此整个类的圈复杂度很高。如果使用 Cobertura 等工具(它们报告类的平均圈复杂度),那么这个类看起来并不糟糕,因为大量简单方法掩盖了那个复杂方法的复杂度。由于工作单元不匹配,所以同时观察圈复杂度的总值和平均值是有意义的。如果单独考虑它们,可能会得出错误的结论。同时使用这两个指标有助于消除这种可能性。
软件指标与物理指标
在软件中,指标是指应用客观的度量开发工件,从而判断粗粒度的特征。与物理指标(比如量尺)不同,大多数软件指标并不反映真实环境中的一些特征。圈复杂度值(比如 5)没有度量单位;它并不说明代码的任何物理性质。这个数字只在与其他代码的圈复杂度进行比较时才有意义。
对于设计有意义的其他指标是两个耦合数:传出(efferent) 和传入 耦合。传出耦合度量当前类引用的类的数量。很容易通过简单的检查判断传出耦合:打开要检查的类,统计(字段和参数中)对其他类的引用。传入耦合比较难判断,但是更有价值。它度量使用当前类的其他类的数量。可以使用命令行 fu 判断它,也可以使用理解这个指标的其他工具。这种工具包括 ckjm,这是一个用于运行 Chidamber & Kemerer 面向对象指标集的开放源码工具。尽管 ckjm 的设置和运行有点儿复杂,但是能够提供圈复杂度(报告类中所有方法的圈复杂度总和)以及传出和传入耦合数。
但是,获得了这些数字之后,如何发挥它们的作用呢(尤其在设计方面)?指标数字只提供关于代码的一个信息维,数字本身往往意义不大。可以以两种方式通过指标获得有用的信息。一种方式是观察数值随时间的变化和趋势。还可以把指标组合起来,提供更丰富的信息,本文介绍这种方式。
指标和设计
我在本系列的几篇文章中以 Struts 代码库作为示例,这不是因为我偏爱 Struts,而是因为它是一个著名的开放源码项目。相信我:在世界上的大多数代码中都能够找到隐藏的设计特征!既然前面使用 Struts,本文继续使用它说明我的观点。
ckjm 的输出是文本,这些文本可以转换为 XML(也可以使用 XSLT 转换为其他格式)。图 1 显示几个 ckjm 指标的组合,其中的 WMC (Weight Methods per Class) 是类的方法的圈复杂度,Ca 是传入耦合。
图 1. 表格形式的 ckjm 指标结果
图 2 显示按 WMC 排序的同一个表:
图 2. 按 WMC 排序的 ckjm 指标