博客 阅读时间 4 分钟

深入了解您的 C++ 项目内部:POCO 案例研究

分享本文
Discover your C++ project internals: POCO case study

POCO C++库是一组用于开发以网络为中心、可移植的 C++ 应用程序的开源类库集合。

POCO 代表可移植组件(POrtable COmponents)。这些库提供了线程、线程同步、文件系统访问、流、共享库和类加载、套接字和网络协议(HTTP、FTP、SMTP 等)等功能。它们还包括一个 HTTP 服务器、一个带有 SAX2 和 DOM 接口的 XML 解析器,以及 SQL 数据库访问功能。

模块化且高效的设计和实现,使 POCO C++ 库非常适合嵌入式开发。

让我们使用CppDepend深入 POCO 内部,了解其实现和设计中的一些事实。

POCO 的实现

代码行数

代码行数很多的方法难以理解和维护。让我们搜索超过 60 行的方法。

不到 1% 的方法超过 60 行。

圈复杂度

圈复杂度是一个流行的过程式软件度量,等于一个过程中可能发生的决策数量。

让我们执行下面的 CQLinq 查询,检测需要重构的方法:

所以,只有 1% 的方法可以被认为是复杂的。

哪些方法既复杂又文档不足?

变量过多的方法

局部变量数 NbVariables 大于 8 的方法难以理解和维护。NbVariables 大于 15 的方法则极其复杂,应该拆分为更小的方法(除非它们是由工具自动生成的)。

只有 8 个方法变量过多。

方法和字段过多的类型

只有 3% 的类型拥有大量方法。

我们可以对字段做同样的搜索。

不到 1% 的类型拥有大量字段。

我们可以得出结论:POCO 实现得很好——很少有方法被认为是复杂的,它的类型简单且方法和字段相对较少,代码也有良好的文档。

设计

抽象性与不稳定性

"抽象性与不稳定性"(Abstractness vs Instability) 图对于检测难以维护或演进的项目很有用。下面这篇文章介绍了这张图的用途,以及如何利用它来改进设计。

对于 POCO,下面是"抽象性与不稳定性"图:

只有 Foundation 位于痛苦区(Zone of Pain)内,这是可以理解的,因为它被其他项目大量使用。

继承

多重继承会增加复杂性,因此应谨慎使用。

让我们搜索拥有多个基类的类。

蓝色矩形代表查询结果。

只有少数类继承自多个类。

类型内聚性

单一职责原则指出,一个类不应该有超过一个变更的理由。这样的类被称为内聚的。高 LCOM 值通常表明类的内聚性较差。LCOM 度量有好几种。LCOM 的取值范围是 [0-1]。LCOMHS(HS 代表 Henderson-Sellers) 的取值范围是 [0-2]。请注意,LCOMHS 度量通常被认为在检测非内聚类型方面更有效。LCOMHS 值高于 1 应被视为警示信号。

只有 1% 的类型被认为是非内聚的。

扇出耦合

某个特定类型的扇出耦合(efferent coupling)是它直接依赖的类型数量。TypeCe > 50 的类型依赖了太多其他类型。它们很复杂,承担了不止一个职责。它们是重构的良好候选。

让我们执行下面的 CQLinq 查询。

结果为空,所以没有任何类承担过多职责。

最常用的类型

了解哪些类型被最频繁地使用是很有用的;为此,我们可以使用 TypeRank 度量。

TypeRank 值是通过将谷歌 PageRank 算法应用于类型依赖图计算得出的。应用了以 0.15 为中心的相似变换,使平均 TypeRank 为 1。

TypeRank 高的类型应该经过更仔细的测试,因为这类类型中的 Bug 可能会造成更严重的后果。

让我们搜索既被大量使用又很复杂的类型。

结果为空,所以没有任何类既被大量使用又很复杂。

分层与层级度量

这篇文章解释了层级(level)度量,以及如何利用它来改进设计。

让我们搜索循环依赖;为此,我们可以执行下面的 CQLinq 查询:

只有少数方法涉及循环依赖。让我们以 Zip 项目为例,看看它的依赖图。

这个项目中只存在 1 个循环依赖。

总而言之,POCO 的设计也很出色:它具有高内聚、低耦合的特点。

分享本文