耦合有强弱梯度
耦合不是有或无,而是一条从强到弱的梯度:最恶劣的是直接修改对方内部数据,往下依次是共享可变全局状态、用标志位控制对方分支、传递复杂内部结构,最轻的是只通过参数或消息交互。日常解耦按三步走——不碰对方内部、只依赖稳定接口、限制依赖方向。目标不是消除耦合,而是把耦合降到可控且显式的形态。
耦合强度对照
| 类型 | 表现 | 改动时的后果 |
|---|---|---|
| 内容耦合 | 直接修改另一模块的内部数据 | 对方内部一变就崩,且无法通过接口发现 |
| 公共耦合 | 共享可变全局状态 | 并行开发互相踩踏,测试顺序影响结果 |
| 控制耦合 | 传标志位控制对方走哪条分支 | 标志位含义只在双方约定,调用点可读性差 |
| 数据结构耦合 | 依赖对方复杂内部结构传递数据 | 结构变更波及所有调用点 |
| 消息耦合 | 仅通过参数或消息交互 | 影响面局限在约定的参数范围内 |
典型使用场景
- 脚本直接改另一个模块的全局变量:先收敛为由所有者提供访问方法
- 函数传布尔参数控制分支:拆成两个命名清晰的方法,调用点自解释
- 两个模块共享同一份可变配置:改为依赖注入,明确所有权与生命周期
- 服务之间共享数据库表:改为通过接口访问,避免表结构变更直接冲击上游
- 包之间出现循环依赖:抽出稳定的中间接口,把依赖方向导正
解耦方向
解耦方向
# 按耦合类型选对应手段
内部数据 → 收敛到访问器,所有权集中在模块内部
全局可变状态 → 依赖注入,生命周期由容器管理
标志位参数 → 拆成命名明确的独立方法
复杂结构传递 → 定义稳定契约对象,只暴露必要字段
双向依赖 → 抽出接口或引入事件,导正依赖方向度量抓手
| 扇入 | 被多少模块引用,数值高说明是稳定核心,改动需重评审 |
|---|---|
| 扇出 | 引用多少模块,数值高说明自身脆弱、易受影响 |
| 循环依赖 | 包之间是否存在环,出现即需优先处理 |
| 跨层依赖 | 上层是否直接依赖了底层实现细节 |
追求绝对零耦合没有意义:模块之间总要通过某种约定协作,完全解耦的系统无法组装出功能。真正需要治理的是不可见、不可替换、方向混乱的耦合。
解耦时优先处理方向问题:把双向依赖或循环依赖导正为单向,收益通常大于细化接口,因为方向错误会让每次改动都产生扩散效应。
落地检查清单
- 依赖关系能在图上画出来,不存在环路
- 跨模块调用只依赖公开接口,不触及内部数据结构
- 共享可变状态的数量在减少或已被明确封装
- 参数列表中没有含义模糊的布尔标志位