重复代码为什么是问题
同一段逻辑出现在多处时,每一次修改都要同步所有副本,漏掉任何一处都会留下行为不一致的隐患。重复代码的常见形态有三种:同一个函数内部的重复片段、兄弟类之间的相同实现、以及看起来无关的类里出现的同一段逻辑。它们对应的处理手法不同,但目标一致——让逻辑只有一个可修改的地方。
基本信息
| 形态 | 同函数内重复、兄弟类之间重复、无关类之间重复 |
|---|---|
| 常用手法 | 提炼函数 Extract Function、上移方法 Pull Up Method、提炼类 Extract Class |
| 判断前提 | 两段代码是否真的会朝同一方向演化 |
| 注意 | 表面相似但语义不同的代码不要硬合并 |
三种形态与对策
- 同一函数内的重复片段:提炼成一个命名清晰的私有函数。
- 兄弟类之间的相同实现:上移到共同父类,或抽出共享的辅助类。
- 无关类之间的重复逻辑:放到一个职责明确的工具类或领域服务里。
- 结构性重复(相似的条件判断反复出现):考虑用策略或查表替代分支。
- 测试代码中的重复:抽公共构造与断言辅助,让用例聚焦在差异上。
提取前
提取前
double a = order.total() * 0.95;
// ... 另一处
result = amount * 0.95;提取后
提取后
double applyDiscount(double amount) {
return amount * 0.95;
}什么时候不该硬合并
| 情形 | 处理 |
|---|---|
| 相似但演化方向不同 | 保留两份,各自演化 |
| 只是循环结构相似 | 结构相似不等于逻辑重复 |
| 合并需要大量参数开关 | 说明抽象选错了,先找真正的共同点 |
| 合并后跨了模块边界 | 先确认依赖方向是否可接受 |
怎么发现重复
- 改动时发现「另一处也要改」:这是重复最可靠的信号。
- 缺陷修复要改多个文件:同一条规则被复制到了多处。
- 代码搜索相似常量、相似的判断条件与相同的注释。
- 看提交历史:同一个文件在几次需求里被以相同方式修改。
重复的判定标准是「会一起变化」,而不是「看起来一样」:两种代码若各自独立演化,合并反而制造耦合。
先提炼再谈去处:把重复片段提取成函数之后,更容易看清它属于哪个类或哪个模块。