C++ 代码整洁之道:C++17 可持续软件开发模式实践 (31):原则 3.6

阅读数:9 2019 年 12 月 4 日 18:49

C++代码整洁之道:C++17可持续软件开发模式实践(31):原则 3.6

(高内聚原则)

内容简介
本书致力于讲述 C++ 整洁代码之道!如果你想让自己写的代码更加整洁,那么这本书适合你阅读。本书需要熟悉 C++ 语言的基本概念,才能有效的掌握其中的内容。如果你只是想从 C++ 开发开始,并且没有 C++ 语言的基础知识,你应该首先选择一个好的 C++ 入门的练习项目。此外,本书也不包含任何深奥的技巧和杂乱的知识点。我知道 C++ 有很多令人兴奋的技巧,但这些通常不是整洁代码的精神,也不是现代 C++ 的代码风格。除此之外,这本书为了帮助 C++ 程序员提高技能水平,并举例说明如何编写易于理解的、灵活的、可维护的和高效的 C++ 代码。即使你是一个经验丰富的 C++ 开发人员,这本书中也有一些值得学习的地方,我认为这些值得学习的地方能够促进你的工作。书中所提出的原则和实践可以应用于新的软件系统,有时被称为“绿地项目”,以及具有悠久历史的遗留系统,通常被称为“棕地项目”。

软件开发中的一条通用建议是,任何软件实体(如模块、组件、单元、类、函数等)应该具有很高的(或强的)内聚性。一般来讲,当模块实现定义确切的功能时,应该具有高内聚的特性。

为了深入研究该原则,让我们来看两个例子,这两个例子没有太多的关联,从图 3-1 开始。

C++代码整洁之道:C++17可持续软件开发模式实践(31):原则 3.6

图 3-1 MyModule 类有很多的职责,这就导致了模块相互依赖

在上面的例子中,模块随意划分,业务领域三个不同的功能放在了一个模块内。功能 A、功能 B 和功能 C 之间基本没有什么共同点,但是这三个功能却被放在 MyModel 模块中。阅读模块的代码就会发现,功能 A、功能 B 和功能 C 在不同的、完全独立的数据上运行。

现在,观察图 3-1 中所有的虚线箭头,箭头指向的每一个模块都是一个被依赖者,箭头尾部的模块需要箭头指向的模块来实现。在这种情况下,系统中的其他模块想要使用功能 A、功能 B 或功能 C 时,调用的模块就会依赖于整个 MyModule 模块。这样设计的缺点是显而易见的:这会导致太多的依赖,并且可维护性也会降低。

为了提高内聚性,功能 A、功能 B 和功能 C 应该彼此分离(见图 3-2)。

C++代码整洁之道:C++17可持续软件开发模式实践(31):原则 3.6

图 3-2 高内聚:将之前混合在一起的 A、B、C 分离成不同的模块

现在,很容易地看出,每个模块的依赖项比旧的 MyModule 的依赖项少得多。很明显,模块 A、模块 B、模块 C 之间没有直接的关系。Module1 是唯一依赖模块 A、模块 B 和模块 C 的模块。

另外一个低内聚的形式是散弹枪反模式(Shot Gun Anti-Pattern)。我想大家应该都听说过,霰弹枪是一种能发射大量小铁沙的武器,这种武器通常有很大的散射性。在软件开发中,这种隐喻用于描述某个特定领域方面或单个逻辑思想是高度碎片化的,并且分布在许多模块中,图 3-3 描述了这种情况。

在这种低内聚方式下,出现了许多不利的依赖关系,功能 A 的各个片段必须紧密结合在一起。这就意味着,实现功能 A 子集的每个模块必须至少与一个包含功能 A 子集的模块交互。这会导致大量的依赖性交叉。最坏的情况是导致循环依赖;比如,模块 1 和模块 3 之间,或模块 6 和模块 7 之间。这再一次对可维护性和可扩展性产生了负面影响。当然,这种设计的可测试性也是非常差的。

这种设计将导致所谓的“霰弹枪手术”。对功能 A 的某种修改会导致很多模块进行或多或少的修改,这真的很糟糕,并且应该避免。我们应该把与功能 A 相关的所有代码都拿出来,把相同逻辑的代码放到一个高内聚的模块内。

一些其他的原则——例如,面向对象设计的单一职责(SRP)原则(详见第 6 章),会促进高内聚性。高内聚往往与松耦合相关,反之亦然。

C++代码整洁之道:C++17可持续软件开发模式实践(31):原则 3.6

图 3-3 功能 A 跨越了 5 个模块

C++代码整洁之道:C++17可持续软件开发模式实践(31):原则 3.6

购书地址 https://item.jd.com/12599914.html?dist=jd

评论

发布