本系列文章通过逐章回答《Fundamentals of Software Architecture》(下文简称 FOSA)一书中的课后思考题,来深入理解书中的核心概念和理论,从而提升我们的软件架构设计能力。本篇为第三章内容。
本章的课后题是:
What is meant by the term connascence?
"连接性"这个术语是什么意思?
What is the difference between static and dynamic connascence?
静态连接性和动态连接性有什么区别?
What does connascence of type mean? Is it static or dynamic connascence?
类型连接性是什么意思?它是静态的还是动态的?
What is the strongest form of connascence?
连接性最强的形式是什么?
What is the weakest form of connascence?
连接性最弱的形式是什么?
Which is preferred within a code base—static or dynamic connascence?
在一个代码库中,静态连接性和动态连接性哪个更受青睐?
本篇的主题是 Modularity(模块化),谈到模块化,我们最常提到的一句话就是:"高内聚(cohesion)、低耦合(coupling)"。
- 内聚是指模块内部各元素之间关联的紧密程度。高内聚意味着模块中的所有元素都紧密相关,并且共同为一个单一的、明确定义的目的服务。高内聚使模块的功能职责更加集中和明确,易于理解和修改。
- 耦合是指不同模块之间相互依赖的程度。低耦合意味着模块之间的依赖关系很弱,一个模块的改变对其他模块的影响尽可能小。低耦合降低了系统修改、测试和部署的风险。当系统的一个部分发生变化时,由于依赖关系较少,需要修改的其他部分也较少。
课后题中提到的 connascence(连接性)跟耦合的概念很像。
当一个组件的修改需要修改另外一个组件才能保持系统整体的正确性时,这两个组件就处于连接性状态。它衡量了软件组件之间相互依赖的程度。
连接性是对耦合度量的一种细化。虽然传统的耦合度量(如内向耦合和外向耦合)关注的是连接的方向,但连接性更进一步,关心组件之间如何耦合在一起。
连接性可以分为 2 大类:
- 静态连接性:指的是源代码级别的耦合,它可以通过简单的源代码分析来确定。
- 命名连接性:多个组件必须就某个实体的名称达成一致。这是最弱的连接性形式,也是代码库中最理想的耦合方式,因为现代重构工具可以轻松实现系统范围内的名称更改。
- 类型连接性:多个组件必须就某个实体的类型达成一致。这在许多静态类型语言中很常见,例如变量和参数被限制为特定类型。
- 意义连接性 / 约定连接性:多个组件必须就某个值的含义或约定达成一致。
- 位置连接性:多个组件必须就某个实体在列表、记录或参数中的位置达成一致。
- 算法连接性:多个组件必须就某个特定算法达成一致。
- 执行连接性:多个组件必须就某个执行顺序或流程达成一致。
- 动态连接性:指的是运行时的调用,这种很难确定,因为缺乏有效的运行时分析工具。
- 执行连接性:不同语句的执行顺序很重要。例如一段代码中某些属性必须按特定顺序设置才能正确运行。
- 时序连接性:多个组件的执行时序很重要。通常发生在竞态条件中,即两个线程同时执行并影响联合操作的结果
- 值连接性:多个值之间相互关联,必须一起改变。
- 身份连接性:多个组件必须引用同一个实体。例如,两个独立的组件必须共享和更新一个公共数据结构。
连接性的强弱如下图所示:

越往左上角,表示连接性越弱,也是我们编写代码时更希望看到的。
Page-Jones 提出了 3 个使用连接性来改善系统模块化的指导原则:
- 通过将系统分解为封装的元素来最小化整体连接性;
- 最小化任何剩余的跨越封装边界的连接性;
- 最大化封装边界内的连接性。
Jim Weirich 进一步给出了 2 条建议:
- 程度规则(Rule of Degree):将强形式的连接性转换为弱形式的连接性。例如,可以通过重构将意义连接性(CoM)转换为命名连接性(CoN),即创建命名常量而不是使用"魔法值"。
- 局部性规则(Rule of Locality):随着软件元素之间距离的增加,使用弱形式的连接性。这意味着,如果组件彼此远离,则它们之间的耦合应尽可能松散,避免强连接性形式。