代码的坏味道(14)——重复代码(Duplicate Code)
坏味道——重复代码(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)的更多相关文章
- 【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)
膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...
- 重构 之 总结代码的坏味道 Bad Smell (一) 重复代码 过长函数 过大的类 过长参数列 发散式变化 霰弹式修改
膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 . 代码的坏味道就意味着需要重构, 对代码的坏味道了然于心是重构的比要前提; . 作者 : 万境绝尘 转载请注明出 ...
- Chapter 3 :代码的坏味道
"如果尿布臭了,就换掉它." --Beck奶奶,论保持小孩清洁的哲学 代码的坏味道这一章集中论述该何时重构.具体的重构方法在后面的章节. "没有任何度量规矩比得上见识广博 ...
- Bad Smell (代码的坏味道)
sourcemaking 如果一段代码是不稳定或者有一些潜在问题的,那么代码往往会包含一些明显的痕迹.正如食物要腐坏之前,经常会发出一些异味一样, 我们管这些痕迹叫做 "代码异味" ...
- Refactoring之——代码的坏味道(一)过长方法
1 代码的坏味道 重构一书中提到了22种代码的坏味道,大致可以分为几类. 识别代码的坏味道,有助于发现代码的潜在问题,从而可以有的放矢的修改现有代码,使之不断完善. 1.1 Bloaters(臭鲱,暂 ...
- 消灭 Java 代码的“坏味道”
消灭 Java 代码的“坏味道” 原创: 王超 阿里巴巴中间件 昨天 导读 明代王阳明先生在<传习录>谈为学之道时说: 私欲日生,如地上尘,一日不扫,便又有一层.着实用功,便见道无终穷,愈 ...
- 【转】Bad Smell(代码的坏味道)
1.Duplicated Code(重复的代码) 臭味行列中首当其冲的就是Duplicated Code.如果你在一个以上的地点看到相同的程序结构,那么当可肯定:设法将它们合而为一,程序会变得更好. ...
- 代码的坏味道(2)——过大的类(Large Class)
坏味道--过大的类(Large Class) 特征 一个类含有过多字段.函数.代码行. 问题原因 类通常一开始很小,但是随着程序的增长而逐渐膨胀. 类似于过长函数,程序员通常觉得在一个现存类中添加新特 ...
- 解析大型.NET ERP系统 代码的坏味道
1 对用户输入做过多的约定和假设 配置文件App.config中有一个设定报表路径的配置节: <add key="ReportPath" value="C:\Us ...
随机推荐
- B树——算法导论(25)
B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...
- 细说前端自动化打包工具--webpack
背景 记得2004年的时候,互联网开发就是做网页,那时也没有前端和后端的区分,有时一个网站就是一些纯静态的html,通过链接组织在一起.用过Dreamweaver的都知道,做网页就像用word编辑文档 ...
- TODO:macOS上ThinkPHP5和Semantic-UI集成
TODO:macOS上ThinkPHP5和Semantic-UI集成 1. 全局安装 (on OSX via homebrew)Composer 是 homebrew-php 项目的一部分 2. 把X ...
- 【WCF】使用“用户名/密码”验证的合理方法
我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...
- PowerDesigner-VBSrcipt-自动设置主键,外键名等(SQL Server)
在PowerDesigner中的设计SQL Server 数据表时,要求通过vbScript脚本实现下面的功能: 主键:pk_TableName 外键:fk_TableName_ForeignKeyC ...
- 【原创经验分享】WCF之消息队列
最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...
- 走进缓存的世界(三) - Memcache
系列文章 走进缓存的世界(一) - 开篇 走进缓存的世界(二) - 缓存设计 走进缓存的世界(三) - Memcache 简介 Memcache是一个高性能的分布式内存对象缓存系统,用于动态Web应用 ...
- EC笔记:第4部分:22、所有成员都应该是private的
EC笔记:第4部分:22.所有成员都应该是private的 更简单的访问 用户不用记得什么时候该带上括号,什么时候不用带上括号(因为很确定的就要带上括号) 访问限制 对于public的成员变量,我们可 ...
- css选择器
常用css选择器,希望对大家有所帮助,不喜勿喷. 1.*:通用选择器 * { margin: 0; padding: 0; } 选择页面上的全部元素,通常用于清除浏览器默认样式,不推荐使用. 2.#i ...
- Android中访问sdcard路径的几种方式
以前的Android(4.1之前的版本)中,SDcard路径通过"/sdcard"或者"/mnt/sdcard"来表示,而在JellyBean(安卓4.1)系统 ...