重复代码
- 含义:
干同一件事的代码在项目中的同一个类/兄弟类/毫不相关的类,重复出现。 - 坏处:
可读性差,重复劳动,可能造成性能降低等 - 目标:
设法合到一块,使其可以复用。 - 实现方法:
- 针对同一个类中两个函数含有相同表达式:
Extract Method(提炼函数) - 针对两个兄弟子类含相同表达式:
- 对两个类都使用Extract Method(提炼函数),然后再使用Pull Up Method(函数上移)
- 如果函数只是类似,并不完全相同,可以将相似部分和差异部分割开,构成单独一个函数,运用Form Template Method (塑造模板函数)
- 如果有些函数以不同的算法做相同的事,可以选其中一个比较清晰的,使用 Substitude Algorithm(替换算法) 将其他函数的算法替换掉。
- 针对两个毫不相关的类:
可以考虑对其中一个使用Extract Class (提炼类), 将重复代码提炼到独立类中,然后在另一个类内使用这个新类。也许还会有更复杂的情况,最终目的是使新类可以公用,并不会出现在其他地方。
- 针对同一个类中两个函数含有相同表达式:
本文涉及的重构方法
Extract Method (提炼函数)
- 作用:
消除做同样事代码,并使其功能可以复用。 - 步骤:
- 创造一个新方法,从这个方法"做什么"来命名,而不是"怎样做"。
- 复制原方法代码,并处理方法内的变量,使其可以保证新方法的通用性。变量的处理可能会涉及到下面几个方法:
- Split Temporary Variable (分解临时变量)
- Replace Temp with Query (以查询取代临时变量)
- 替换原代码中使用此方法的地方为新的方法。
- 编译并测试
- 重构思路:
把代码中实现相同功能的部分抽取出来,提炼为一个可以共用的方法,然后把源代码的该部分替换为这个共用方法,如果涉及到参数传递的,需要做出对应修改。
建议:
- 根据项目情况合理设置方法的作用域
- 需要多个返回参数的时候,考虑使用Replace Temp with Query (以查询取代临时变量),或者 Replace Method with Method Object (以函数对象取代函数)
Pull Up Method (函数提升)
- 作用:
使用继承来消除多个子类的同样功能代码,使其方法可以复用。 - 步骤:
- 检查这些函数是否做的事是一样的,如果看上去一样,实际不完全一致,可使用 Substitute Algorithm (替换算法)。
- 如果方法名不同,考虑一下在提升以后的父类中统一方法名
- 如果该函数有用到子类的其他字段,考虑参考2个方法:
- Pull Up Field (字段上移)
- Self Encapsulate Field (自封装字段)
- 移除原来的函数,多测试。
- 重构思路:
通过继承来消除多个类中做相同事的代码,Pull Up的意思就是把一个class里的method提升到一个superclass中。
Form Template Method (塑造模板函数)
- 作用:
消除那些操作大致相同,具体实现不同的方法,避免代码重复 - 步骤:
- 在各个子类里分解那些函数,把实现一致的函数的命名统一,实现不一致的那部分函数命名区别开。
- 对于那些完全不同的函数,使用Rename Method (函数改名),使所有的这些不同的函数名完全相同
- 运用Pull Up Method(函数提升) ,把所有函数移到超类中,将超类里那些代表不同操作的函数定义为抽象函数
- 移除原来的函数,多测试。
- 重构思路:
通过继承,把多个子类相同方法统一命名,不同的方法也统一命名,相同方法移到父类中时,使用一个名字,不同方法移到父类中时,使用不同方法并声明为抽象函数。
建议:
- 参考设计模式中的模板模式
Substitude Algorithm (替换算法)
- 作用:
代码更清晰,精炼。 - 步骤:
- 写好另一个算法,测试,如果可以完全替代原来的算法,则替换。
- 重构思路:
使用更清晰精炼的代码实现来重构原有的代码逻辑。比如java8中提供stream()方法处理数据会比原来的实现精炼很多
建议:
- 多使用类库来实现,可以精炼现有代码。
- 保持清晰度,不能过于舍弃代码可读性来追求精炼度。
Extract Class (提炼类)
- 作用:
每个类各司其职,代码结构更清晰 - 步骤:
- 理清并分解类所负的责任
- 建立一个新类,把责任归类并分离出来,如果类名已经不能符合原本指责范围,则需要重命名。
- 通过函数方法来提供旧类获取新类的数据。
- 搬移各个字段,可以运用 Move Field(搬移字段) 和 Move Method (搬移函数) 来把原来类中的方法和字段搬到新类里
- 如果这些类中有互相获取数据的方法,尽量考虑改为单向获取数据的方法。
- 合理设置这些类的作用域
- 重构思路:
使用更清晰精炼的代码实现来重构原有的代码逻辑。比如java8中提供stream()方法处理数据会比原来的实现精炼很多
建议:
- 如果存在多线程并发或者事物的问题,需要多考虑是否使用此方法及如何使用。