博客 阅读时间 6 分钟

向 C++ POCO 库学习如何编写整洁代码

分享本文
Learn from the C++ POCO Libraries How to Write Clean Code

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

POCO 是“可移植组件”(POrtable COmponents)的缩写。这些库涵盖的功能包括:线程、线程同步、文件系统访问、流、共享库与类加载、套接字与网络协议(HTTP、FTP、SMTP 等),并包含一个 HTTP 服务器、一个带 SAX2 和 DOM 接口的 XML 解析器,以及 SQL 数据库访问。

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

让我们来看一段 POCO 源代码的片段:

这个实现具有以下特点:

  • 函数只有寥寥几个参数。
  • 使用断言来验证输入是否有效。
  • 变量命名易于理解。
  • 方法短小。
  • 函数体内没有多余的注释:代码自己会说话。
  • 函数体缩进良好。
  • 在恰当的地方使用了成熟的 STL。

浏览 POCO 源代码时,我们可以看到实现的一致性:同样的最佳实践规则被应用到每一个函数上。

让我们借助CppDepend深入 POCO 内部,发掘它在设计和实现上的一些事实。

设计

抽象度与不稳定度

Robert C. Martin 写过一篇有趣的文章,介绍了一组度量,可用于从设计中各子系统之间相互依赖的角度,衡量面向对象设计的质量。

下面是他在文章中关于模块间相互依赖的论述:

是什么让一个设计变得僵化、脆弱且难以复用?是设计中各子系统之间的相互依赖。如果一个设计无法轻易改变,它就是僵化的。这种僵化源于:在高度相互依赖的软件中,一次变更就会引发依赖模块中的一连串变更。当设计师或维护者无法预测这一连串变更的范围时,变更的影响就无法评估。

为了对抗僵化,他引入了传入耦合、传出耦合、抽象度、不稳定度、“与主序列的距离”以及“抽象度-不稳定度图”等度量。

“抽象度-不稳定度图”有助于识别难以维护和演进的项目。下面是 POCO 库的“抽象度-不稳定度图”:

这张图背后的思想是:程序中的一个代码元素越热门,它就应该越抽象。换句话说,避免过于直接地依赖实现;转而依赖抽象。这里所说的热门代码元素,指的是被程序中其他项目大量使用的项目(不过这个思想同样适用于包和类型)。让具体类型在代码库中被广泛使用并不是好主意。这会形成

上图中的主序列线(虚线)展示了抽象度与不稳定度应如何平衡。稳定的组件会位于左侧。查看主序列线可以发现:这样的组件应当非常抽象,才能接近理想的线——反过来说,如果它的抽象程度很低,就会落在一个被称为“痛苦地带”的区域。

只有 Foundation 项目处于痛苦地带内,这是正常的,因为它被其他项目广泛使用。

继承

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

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

蓝色矩形代表查询结果。

只有少数类派生自多个类。

类型内聚性

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

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

传出耦合

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

让我们执行以下 CQLinq 查询。

结果为空,因此没有类承担过多职责。

使用最多的类型

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

TypeRank 值是通过将 Google PageRank 算法应用于类型依赖关系图计算出来的。计算时应用了中心为 0.15 的位似变换,使平均 TypeRank 为 1。

TypeRank 高的类型应当被更仔细地测试,因为这类类型中的缺陷很可能造成更严重的后果。

让我们搜索使用最广泛且复杂的类型。

结果为空,因此没有类既被广泛使用又复杂。

分层与层级度量

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

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

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

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

POCO 的实现

代码行数

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

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

圈复杂度

圈复杂度是一种流行的过程式软件度量,衡量一个过程中独立路径的数量。

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

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

哪些方法既复杂又注释不足?

变量很多的方法

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

只有 8 个方法的变量过多。

拥有大量方法和字段的类型

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

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

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

我们可以得出结论:POCO 的实现非常出色——被认为是复杂的方法很少,它的类型简单且方法和字段相对较少,代码的注释也很到位。

分享本文