坏味道——重复代码(Duplicate Code)

重复代码堪称为代码坏味道之首。消除重复代码总是有利无害的。

特征

两个代码片段看上去几乎一样。

问题原因

重复代码通常发生在多个程序员同时在同一程序的不同部分上工作时。由于他们正在处理不同的任务,他们可能不知道他们的同事已经写了类似的代码。

还有一种更隐晦的重复,特定部分的代码看上去不同但实际在做同一件事。这种重复代码往往难以找到和消除。

有时重复是有目的性的。当急于满足deadline,并且现有代码对于要交付的任务是“几乎正确的”时,新手程序员可能无法抵抗复制和粘贴相关代码的诱惑。在某些情况下,程序员只是太懒惰。

解决方法

  • 同一个类的两个函数含有相同的表达式,这时可以采用 提炼函数(Extract Method) 提炼出重复的代码,然后让这两个地点都调用被提炼出来的那段代码。

  • 如果两个互为兄弟的子类含有重复代码:
  • 首先对两个类都运用 提炼函数(Extract Method) ,然后对被提炼出来的函数运用 函数上移(Pull Up Method) ,将它推入超类。
  • 如果重复代码在构造函数中,运用 构造函数本体上移(Pull Up Constructor Body)
  • 如果重复代码只是相似但不是完全相同,运用 塑造模板函数(Form Template Method) 获得一个 模板方法模式(Template Method)
  • 如果有些函数以不同的算法做相同的事,你可以选择其中较清晰地一个,并运用 替换算法(Substitute Algorithm) 将其他函数的算法替换掉。
  • 如果两个毫不相关的类中有重复代码:
  • 请尝试运用 提炼超类(Extract Superclass) ,以便为维护所有先前功能的这些类创建一个超类。
  • 如果创建超类十分困难,可以在一个类中运用 提炼类(Extract Class) ,并在另一个类中使用这个新的组件。
  • 如果存在大量的条件表达式,并且它们执行完全相同的代码(仅仅是它们的条件不同),可以运用 合并条件表达式(Consolidate Conditional Expression) 将这些操作合并为单个条件,并运用 提炼函数(Extract Method) 将该条件放入一个名字容易理解的独立函数中。
  • 如果条件表达式的所有分支都有部分相同的代码片段:可以运用 合并重复的条件片段(Consolidate Duplicate Conditional Fragments) 将它们都存在的代码片段置于条件表达式外部。

收益

  • 合并重复代码会简化代码的结构,并减少代码量。
  • 代码更简化、更易维护。

重构方法说明

提炼函数(Extract Method)

问题

你有一段代码可以组织在一起。

void printOwing() {
  printBanner();

  //print details
  System.out.println("name: " + name);
  System.out.println("amount: " + getOutstanding());
}

解决

移动这段代码到一个新的函数中,使用函数的调用来替代老代码。

void printOwing() {
  printBanner();
  printDetails(getOutstanding());
}

void printDetails(double outstanding) {
  System.out.println("name: " + name);
  System.out.println("amount: " + outstanding);
}

函数上移(Pull Up Method)

问题

有些函数,在各个子类中产生完全相同的结果。

解决

将该函数移至超类。

构造函数本体上移(Pull Up Constructor Body)

问题

你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。

class Manager extends Employee {
  public Manager(String name, String id, int grade) {
    this.name = name;
    this.id = id;
    this.grade = grade;
  }
  //...
}

解决

在超类中新建一个构造函数,并在子类构造函数中调用它。

class Manager extends Employee {
  public Manager(String name, String id, int grade) {
    super(name, id);
    this.grade = grade;
  }
  //...
}

塑造模板函数(Form Template Method)

问题

你有一些子类,其中相应的某些函数以相同的顺序执行类似的操作,但各个操作的细节上有所不同。

解决

将这些操作分别放进独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。

注:这里只提到具体做法,建议了解一下模板方法设计模式。

替换算法(Substitute Algorithm)

问题

你想要把某个算法替换为另一个更清晰的算法。

String foundPerson(String[] people){
  for (int i = 0; i < people.length; i++) {
    if (people[i].equals("Don")){
      return "Don";
    }
    if (people[i].equals("John")){
      return "John";
    }
    if (people[i].equals("Kent")){
      return "Kent";
    }
  }
  return "";
}

解决

将函数本体替换为另一个算法。

String foundPerson(String[] people){
  List candidates =
    Arrays.asList(new String[] {"Don", "John", "Kent"});
  for (int i=0; i < people.length; i++) {
    if (candidates.contains(people[i])) {
      return people[i];
    }
  }
  return "";
}

提炼超类(Extract Superclass)

问题

两个类有相似特性。

解决

为这两个类建立一个超类,将相同特性移至超类。

提炼类(Extract Class)

问题

某个类做了不止一件事。

解决

建立一个新类,将相关的字段和函数从旧类搬移到新类。

合并条件表达式(Consolidate Conditional Expression)

问题

你有一系列条件分支,都得到相同结果。

double disabilityAmount() {
  if (seniority < 2) {
    return 0;
  }
  if (monthsDisabled > 12) {
    return 0;
  }
  if (isPartTime) {
    return 0;
  }
  // compute the disability amount
  //...
}

解决

将这些条件分支合并为一个条件,并将这个条件提炼为一个独立函数。

double disabilityAmount() {
  if (isNotEligableForDisability()) {
    return 0;
  }
  // compute the disability amount
  //...
}

合并重复的条件片段(Consolidate Duplicate Conditional Fragments)

问题

在条件表达式的每个分支上有着相同的一段代码。

if (isSpecialDeal()) {
  total = price * 0.95;
  send();
}
else {
  total = price * 0.98;
  send();
}

引申阅读

欢迎继续阅读 代码的症与药 系列文章。

代码的坏味道(14)——重复代码(Duplicate Code)的更多相关文章

  1. 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  2. 重构 之 总结代码的坏味道 Bad Smell (一) 重复代码 过长函数 过大的类 过长参数列 发散式变化 霰弹式修改

    膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...

  3. Chapter 3 :代码的坏味道

    "如果尿布臭了,就换掉它." --Beck奶奶,论保持小孩清洁的哲学 代码的坏味道这一章集中论述该何时重构.具体的重构方法在后面的章节. "没有任何度量规矩比得上见识广博 ...

  4. Bad Smell (代码的坏味道)

    sourcemaking 如果一段代码是不稳定或者有一些潜在问题的,那么代码往往会包含一些明显的痕迹.正如食物要腐坏之前,经常会发出一些异味一样, 我们管这些痕迹叫做 "代码异味" ...

  5. Refactoring之——代码的坏味道(一)过长方法

    1 代码的坏味道 重构一书中提到了22种代码的坏味道,大致可以分为几类. 识别代码的坏味道,有助于发现代码的潜在问题,从而可以有的放矢的修改现有代码,使之不断完善. 1.1 Bloaters(臭鲱,暂 ...

  6. 消灭 Java 代码的“坏味道”

    消灭 Java 代码的“坏味道” 原创: 王超 阿里巴巴中间件 昨天 导读 明代王阳明先生在<传习录>谈为学之道时说: 私欲日生,如地上尘,一日不扫,便又有一层.着实用功,便见道无终穷,愈 ...

  7. 【转】Bad Smell(代码的坏味道)

    1.Duplicated Code(重复的代码) 臭味行列中首当其冲的就是Duplicated Code.如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好. ...

  8. 代码的坏味道(2)——过大的类(Large Class)

    坏味道--过大的类(Large Class) 特征 一个类含有过多字段.函数.代码行. 问题原因 类通常一开始很小,但是随着程序的增长而逐渐膨胀. 类似于过长函数,程序员通常觉得在一个现存类中添加新特 ...

  9. 解析大型.NET ERP系统 代码的坏味道

    1  对用户输入做过多的约定和假设 配置文件App.config中有一个设定报表路径的配置节: <add key="ReportPath" value="C:\Us ...

随机推荐

  1. [虾扯蛋] android界面框架-Window

    从纯sdk及framwork的角度看,android中界面框架相关的类型有:Window,WindowManager,View等.下面就以这几个类为出发点来概览下安卓开发的"界面架构&quo ...

  2. Java8实战分享

    虽然很多人已经使用了JDK8,看到不少代码,貌似大家对于Java语言or SDK的使用看起来还是停留在7甚至6. Java8在流式 or 链式处理,并发 or 并行方面增强了很多,函数式的风格使代码可 ...

  3. 2.WindowsServer2012R2装完的一些友好化设置

    网站部署之~Windows Server | 本地部署 http://www.cnblogs.com/dunitian/p/4822808.html#iis 1.桌面图标(控制面板里面屏蔽了,得自己输 ...

  4. Laravel 5.x 请求的生命周期(附源码)

    Laravel最早接触是刚开始实习的时候,那时通过网上的学习资料很快便上手,开发模块接口.后来没有什么深入和总结,但是当我刚开始学Laravel的时候,我对Laravel最大的认识就是,框架除了路由. ...

  5. ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理

    系列目录 前言 回顾上一节,我们熟悉的了解了消息的请求和响应,这一节我们来建立数据库的表,表的设计蛮复杂 你也可以按自己所分析的情形结构来建表 必须非常熟悉表的结果才能运用这张表,这表表的情形涵盖比较 ...

  6. 运用php做投票题,例题

    要求大概是这样的,有一个题目,题目下面是复选框,要求点完复选框提交后会变成进度条,各选项的进度条百分比,和投票数量 首先还是要在数据库建两张表,如下: 要完成这个题目,需要建两个页面 <!DOC ...

  7. 使用RequireJS并实现一个自己的模块加载器 (一)

    RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...

  8. Java进击C#——前言

    本章简言 记得三年前笔者来到现在的公司的时候,公司人口不出十个人.那个时候笔者刚从日本回来,想在福州.厦门.青岛找一个合适自己发展的机会.最后我的一个福州的朋友打电话希望我能过去帮他,跟他一起创业.这 ...

  9. 2013 Asia Changsha Regional Contest---Josephina and RPG(DP)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4800 Problem Description A role-playing game (RPG and ...

  10. UGUI Text(Label)

    环境 Unity 5.3.6f1 关于Best Fit 如果勾选了 Best Fit ,当有大量的文本填充在Text上时,那么文字是不会自动换行的. 打字机效果 在github上已有现成的:https ...