Skip to content

内部产品属性

内部产品属性是无需执行软件或考虑其外部环境,仅从软件工件本身(代码、设计文档等)即可度量的特性。度量这些属性有助于在开发和维护期间监控和控制产品,通常可作为可靠性或可维护性等外部质量属性的先行指标(leading indicators)。

两个基本内部属性是大小和结构。大小通常与开发工作量相关,而结构则严重影响可维护性、可测试性和可理解性。

软件大小是一个多方面的概念,通常通过以下方式描述:

  • 长度(Length):软件工件的物理范围(例如,代码行数、文件/模块数量)。

  • 功能性(Functionality):提供给用户的特性和功能的范围(例如,功能点 - Function Points、Story Points)。

  • 复杂度(Complexity):软件设计和实现的复杂程度。这本身具有多个维度:

    • 问题复杂度(Problem Complexity): 正在解决问题的固有难度。
    • 算法复杂度(Algorithmic Complexity): 所选算法的效率(例如,大 O 标记法 - Big O notation)。
    • 结构复杂度(Structural Complexity): 源于代码结构的复杂度(例如,依赖关系、嵌套级别,在“结构”部分进一步讨论)。
    • 认知复杂度(Cognitive Complexity): 人类理解代码所需的脑力投入。

让我们探讨如何度量大小的这些方面:

长度可针对各种工件进行度量,例如规格说明书、设计文档和代码。早期度量(规格说明书长度)有时可以预测后期的度量(代码长度)。

这些文档通常将文本与图表混合(例如,UML 图、流程图、线框图)。长度可通过计算页数、字数或特定图表元素数量来度量(例如,UML 类数量、用例数量、活动节点数量)。为确保一致性,需要为所用每种标记法中的原子元素定义清晰的计数规则。

最传统的度量方法是代码行数(Lines of Code - LOC)。明确定义 LOC 至关重要:它是否包含注释、空行?是物理行还是逻辑语句?一个常见的区分是:

  • 源代码行数(Source Lines of Code - SLOC):通常不包括注释和空行。
  • 非注释代码行数(Non-Comment Lines of Code - NCLOC):类似于 SLOC。
  • 逻辑代码行数(Logical Lines of Code - LLOC):计算可执行语句,可能跨越多个物理行。

虽然易于自动化,但 LOC 存在显著局限性:它在不同语言之间差异很大,不能准确反映复杂度或功能性,并且可以被操纵。它通常与其他度量指标一起使用。

其他长度度量包括:

  • 文件、模块、类、方法/函数数量。
  • 字节或字符数量(对于代码本身较不常用)。

Halstead 软件科学(历史背景):Maurice Halstead 提出了基于对操作符(例如,+、=、if)和操作数(变量、常量)计数的度量指标。主要概念包括:

  • μ1: 唯一操作符数量
  • μ2: 唯一操作数数量
  • N1: 操作符总出现次数
  • N2: 操作数总出现次数

基于这些,Halstead 推导出了程序长度($$N = N_{1}+ N_{2}$$)、词汇量($$\mu =\mu _{1}+\mu {2}$$)、体积($$V = N\times {log{2}} \mu$$)、难度($$D \approx (\mu_1 / 2) \times (N_2 / \mu_2)$$)和工作量($$E = D \times V$$)等度量。

虽然在历史上具有影响力,试图为度量提供科学基础,但 Halstead 度量计算准确性复杂(一致地定义操作符/操作数很困难),并且与更简单的复杂度度量指标相比,在现代实践中很少使用。

面向对象度量(Object-Oriented Metrics):诸如类数量(Number of Classes - NOC)、每个类的方法数量(Number of Methods per Class)和加权类方法数(Weighted Methods per Class - WMC)等度量在 OO 系统中提供长度/大小的洞察。

功能性度量旨在量化交付给用户的特性,独立于实现细节。常见方法包括:

  • 功能点分析(Function Point Analysis - FPA):一种标准化方法(例如,Albrecht 法、IFPUG、COSMIC),计算用户可识别的功能(如输入、输出、查询、文件和接口),并根据复杂度进行调整。在下一章详细介绍。
  • 用例点(Use Case Points - UCP):基于对 UML 模型中的参与者和用例的计数,并根据复杂度及技术/环境因素进行调整。
  • Story Points(敏捷):敏捷开发中使用的相对度量,用于估计用户故事所需的工作量,结合了复杂度、不确定性和工作量。不是直接的功能大小度量,但相关。

功能性度量通常被认为是比 LOC 更好的用户感知大小指标,但可能更主观且计算耗时。

结构度量评估源于软件内部组织和相互连接的复杂度。高结构复杂度常常导致测试、理解和维护方面的困难。关键度量指标包括:

  • 圈复杂度(Cyclomatic Complexity - McCabe):度量通过一段代码(例如,函数或方法)的线性独立路径数量。从控制流图(节点 - Nodes、边 - Edges)计算得出,公式为 E - N + 2。值越高表示分支逻辑越复杂,暗示更高的风险和所需的测试工作量。静态分析工具常用此度量。
  • 耦合度量(Coupling Metrics):度量模块/类之间的相互依赖程度(例如,面向对象中的对象间耦合 - Coupling Between Objects - CBO)。高耦合使变更产生连锁反应,降低可重用性。
  • 内聚度量(Cohesion Metrics):度量单个模块/类中元素的关联紧密程度(例如,面向对象中的方法内聚不足 - Lack of Cohesion in Methods - LCOM)。低内聚意味着模块/类承担了太多不相关的职责,可能应该被拆分。
  • 嵌套深度(Nesting Depth):嵌套控制结构(if, for, while)的最大层级。深层嵌套使代码难以阅读和理解。
  • 认知复杂度(Cognitive Complexity):一个较新的度量指标(由 SonarQube 等工具推广),旨在度量理解代码所需的脑力投入,对打断线性流程或需要脑力堆栈的结构进行惩罚。
  • 依赖分析(Dependency Analysis):映射模块/组件之间的依赖关系,以识别潜在的循环或过于复杂的关系。

现代静态分析工具可自动计算许多这些结构度量指标,在编码过程中为开发者提供有价值的反馈。