什么是霰弹式修改
霰弹式修改指一个变化需要同时改动很多处:改一个字段要在十几个文件里做相似的修改,漏掉一处就在某条路径上出错。它与发散式变化互为镜像——那里是一个模块因多种原因被修改,这里是同一个原因要改很多模块。处方是把散落的相关代码与数据聚拢到一个模块,让一个概念只留一个修改点。
基本信息
| 典型信号 | 改一个字段或规则要动十几个文件 |
|---|---|
| 处方 | 移动函数、移动字段、合并到同一类、内联多余类 |
| 反向对照 | 发散式变化:一个类因多种原因被反复修改 |
| 判断问题 | 改一处容易漏几处 |
| 收益 | 单一修改点,回归范围可预期 |
典型使用场景
- 新增一种支付方式,发现要在校验、计费、对账、通知四处加分支
- 字段改名需要同步多处数据结构与转换代码
- 税率或有效期规则散落在多个类中,改一次要全体比对
- 同一份常量在多个模块各写一遍,取值不一致时难以发现
- 上线清单要靠人记还有哪些地方要改
聚拢思路
聚拢思路
// 分散:多处各自计算有效期,规则改一次要全部同步
boolean valid(Subscription s) { return now.isBefore(s.getEnd()); }
// 聚拢:一个模块负责有效期这个概念
class Validity {
boolean isValid(Instant now) { ... } // 判断逻辑集中在此
boolean isExpiringSoon(Instant now) { ... }
}
// 调用方只依赖 Validity,规则变化只改这一个类现象与处理
| 现象 | 说明 | 处理 |
|---|---|---|
| 一处改动引发多处相似修改 | 变化没有收拢到单一模块 | 把相关代码与数据移到同一模块 |
| 修改后总有漏掉的点 | 缺少统一入口 | 抽出统一入口,让调用方依赖它 |
| 相似逻辑出现在多个文件 | 概念被复制 | 先合并实现,再考虑参数化差异 |
动手前先做完整盘点:把所有需要同步修改的位置列出来,逐处对照行为是否一致。集中过程中最容易在边角条件上出现静默的行为改变。
集中时不要顺手改行为:先做纯搬迁(结构等价),把行为调整放进单独提交;出问题时才能判断是搬迁引入的还是改动引入的。