共变是什么
共变(Connascence)是比耦合更细致的依赖判断工具:它描述「修改一处必须同步修改另一处」的具体形式。静态共变包括名称、类型、语义、位置与算法;动态共变包括执行、时序、值与身份。相比笼统地说两个模块耦合紧,共变能指出究竟是哪一种依赖让改动必须成对发生,从而给出更具体的重构方向。
怎么用它做判断
共变的代价取决于两个维度:强度与距离。强度越高、跨越的边界越远,维护风险越大。同一模块内的名称共变可以接受,跨服务边界的时序共变则必须转成显式契约,例如把隐式的调用顺序写成状态机或接口约束。重构的目标不是消灭共变,而是把强共变限制在小范围内,或用抽象把它替换成更弱的依赖形式。
基本信息
| 静态共变 | 名称、类型、语义、位置与算法,在编译或阅读阶段可发现 |
|---|---|
| 动态共变 | 执行、时序、值与身份,只在运行期表现出来 |
| 强度判断 | 值与身份类共变强度高,名称与类型相对弱 |
| 距离因素 | 同一模块内可容忍,跨服务边界必须转为显式契约 |
| 重构方向 | 用抽象、契约与封装把强共变限制在小范围内 |
常见形式
| 形式 | 示例 | 风险 |
|---|---|---|
| 名称共变 | 方法名与调用处必须一致 | 改名需全局检索替换 |
| 类型共变 | 参数类型变化要求调用方同步改 | 编译期即可发现 |
| 语义共变 | 变量含义变化但名称未变 | 最隐蔽,容易埋下逻辑错误 |
| 时序共变 | 必须先初始化再调用 | 顺序错时运行期才失败 |
| 值共变 | 多处硬编码同一魔法值 | 改一处漏一处 |
典型使用场景
- 代码评审时判断改动是否必须跨模块成对提交
- 划分服务边界时检查接口上是否存在强共变
- 重构前评估把隐式约定显式化的收益与成本
- 排查偶发故障时检查是否由时序共变引起
共变强度与跨边界距离相关:同一模块内可容忍的强共变,跨服务边界时通常必须转成显式契约,否则每次改动都要多方同步发布。
只关注静态共变会漏掉时序与值层面的问题,这类依赖只在运行期暴露,通常表现为难以复现的偶发故障。