架构风格与模式58 条

共变(Connascence)

比耦合更细致的依赖判断工具:共变指“修改一处必须同步修改另一处”。静态共变包括名称、类型、语义、位置、算法;动态共变包括执行、时序、值、身份。

共变是什么

共变(Connascence)是比耦合更细致的依赖判断工具:它描述「修改一处必须同步修改另一处」的具体形式。静态共变包括名称、类型、语义、位置与算法;动态共变包括执行、时序、值与身份。相比笼统地说两个模块耦合紧,共变能指出究竟是哪一种依赖让改动必须成对发生,从而给出更具体的重构方向。

怎么用它做判断

共变的代价取决于两个维度:强度与距离。强度越高、跨越的边界越远,维护风险越大。同一模块内的名称共变可以接受,跨服务边界的时序共变则必须转成显式契约,例如把隐式的调用顺序写成状态机或接口约束。重构的目标不是消灭共变,而是把强共变限制在小范围内,或用抽象把它替换成更弱的依赖形式。

基本信息

静态共变名称、类型、语义、位置与算法,在编译或阅读阶段可发现
动态共变执行、时序、值与身份,只在运行期表现出来
强度判断值与身份类共变强度高,名称与类型相对弱
距离因素同一模块内可容忍,跨服务边界必须转为显式契约
重构方向用抽象、契约与封装把强共变限制在小范围内

常见形式

形式示例风险
名称共变方法名与调用处必须一致改名需全局检索替换
类型共变参数类型变化要求调用方同步改编译期即可发现
语义共变变量含义变化但名称未变最隐蔽,容易埋下逻辑错误
时序共变必须先初始化再调用顺序错时运行期才失败
值共变多处硬编码同一魔法值改一处漏一处

典型使用场景

  • 代码评审时判断改动是否必须跨模块成对提交
  • 划分服务边界时检查接口上是否存在强共变
  • 重构前评估把隐式约定显式化的收益与成本
  • 排查偶发故障时检查是否由时序共变引起
共变强度与跨边界距离相关:同一模块内可容忍的强共变,跨服务边界时通常必须转成显式契约,否则每次改动都要多方同步发布。
只关注静态共变会漏掉时序与值层面的问题,这类依赖只在运行期暴露,通常表现为难以复现的偶发故障。

易错点

  • 把强共变隐藏在跨团队接口上,每次变更都要多方同步发布
  • 只关注静态共变,忽略时序共变导致的偶发故障
  • 同一魔法值在多处硬编码,改动时漏改一处造成数据错误
  • 语义共变未被识别,变量含义变了但名称没变,长期误导阅读者
  • 用抽象掩盖位置共变,实际调用顺序仍被隐含约束,接口只是表面解耦

数据来源与参考

  • 《软件架构:架构模式、特征及实践指南》(Richards & Ford)
  • Meilir Page-Jones 共变相关论文(1992)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌