首页/开发/代码质量与评审速查/坏味道:重复代码(Duplicated Code)
重构手法62 条

坏味道:重复代码(Duplicated Code)

同一逻辑在多处重复时,修改需要同步多处;先用 Extract Function 合并片段,跨类重复用 Pull Up Method 或 Extract Class;重复的测试代码同样值得处理。

重复代码为什么是问题

同一段逻辑出现在多处时,每一次修改都要同步所有副本,漏掉任何一处都会留下行为不一致的隐患。重复代码的常见形态有三种:同一个函数内部的重复片段、兄弟类之间的相同实现、以及看起来无关的类里出现的同一段逻辑。它们对应的处理手法不同,但目标一致——让逻辑只有一个可修改的地方。

基本信息

形态同函数内重复、兄弟类之间重复、无关类之间重复
常用手法提炼函数 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;
}

什么时候不该硬合并

情形处理
相似但演化方向不同保留两份,各自演化
只是循环结构相似结构相似不等于逻辑重复
合并需要大量参数开关说明抽象选错了,先找真正的共同点
合并后跨了模块边界先确认依赖方向是否可接受

怎么发现重复

  • 改动时发现「另一处也要改」:这是重复最可靠的信号。
  • 缺陷修复要改多个文件:同一条规则被复制到了多处。
  • 代码搜索相似常量、相似的判断条件与相同的注释。
  • 看提交历史:同一个文件在几次需求里被以相同方式修改。
重复的判定标准是「会一起变化」,而不是「看起来一样」:两种代码若各自独立演化,合并反而制造耦合。
先提炼再谈去处:把重复片段提取成函数之后,更容易看清它属于哪个类或哪个模块。

易错点

  • 为消除重复强行抽象:耦合了本会独立演化的两段逻辑,后续改动互相牵制。
  • 复制粘贴后只改一处:漏改处成为潜伏缺陷,等线上暴露时很难定位。
  • 用大量布尔参数区分两种行为:合并了代码却没合并概念,可读性更差。
  • 只盯着代码字面:忽略了同一条业务规则被写在不同层级的情况。
  • 把测试里的重复也硬合并:抽象过度后单个用例的意图被隐藏。

数据来源与参考

  • 《重构》第 2 版 · 第 3 章(Duplicated Code)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌