重构的难题
数据库
- 绝大多数的商用程序都与其背后的DataBase schema(数据库表格结构)紧密耦合在一起,DataBase schema如此难以修改的原因。
- 数据迁移,常用手段是:系统分层–将DataBase schema和对象模型间的依赖降到最低,但是对数据库表的更改,这可能是一件漫长的工作。
在非对象数据库中,解决办法是:在对象模型和数据库模型之间插入一个分隔层,可以隔绝两个模型各自的变化。
修改接口
对于对象:允许分开修改软件模块的实现(implementation)和接口(interface)。
- 对象: 可以安全的修改对象内部而不影响他人
- 接口: 修改接口任何事情都有可能发生
如果某个接口的函数的所有调用者都能够控制,那么修改函数名称不会有任何问题。
当需要修改的接口是无法找到的,并且找到也不能修改的代码是,那么修改接口会成为问题。【已发布接口】
对于已发布的接口:
- 需要同时维护新旧两个接口,让旧接口继续工作
- 让旧接口调用新接口
- 当修改某个函数名称时,先留下旧函数,让它调用新函数
- 使用Java提供的旧接口标记:deprecated
- 尽量少的发布(public)的接口
代码的坏味道
重复的代码
- 同一个class内的两个函数含有相同表达式 ----Extract Method提炼出重复的代码,让这两个地点都调用被提炼出来的那段代码
- 两个子类含有相同的表达式----对两个class使用Extract Method,将其放到父类中
- 代码之间类似----运用Extract Method将相似部分和差异部分分开,用模版设计模式重构
- 两个独立的class内出现重复代码----对其中一个class使用Extract Class,将重复代码提炼到一个独立的class中,然后在另一个class内使用这个新的class。
过长函数
每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途(而非实现手法)命名。99%的场合里,要把函数变小,只需要使用Extract Method。
- 函数内有大量的参数和变量和临时变量----需要把这些参数和临时变量当作参数,传递给新函数,导致可读性几乎没有任何提升
- Introduce Parameter Object
- Preserve Whole Object
- Replace Method with Method Object
- 具体哪一行代码----寻找注释,条件式和循环常常也是提炼的信号
- 条件式----分解条件式
- 循环----将循环和其内的代码提炼到一个独立函数中
过大类
想利用单一class做太多事,往往会出现其太多的Instance变量,重复代码也就接踵而至
- 运用Extract Class将Class内彼此相关的变量,将它们放在一起提炼到新class中。
- class并非在所有时刻都使用所有子类中的变量,可以多次使用Extract Class或Extract Subclass。
- 先确定客服端如何使用它们,然后Extract Interface为每一种使用方式提炼出一个接口,了解如何分解这个class。
过长参数列
全局数据是邪恶的东西,太长的参数列难以理解,太多参数会造成前后不一致、不易使用,而且一旦你需要更多数据,就不得不修改它。
发散式改变
希望软件能够更容易被修改,一旦需要需改,希望能够跳到系统的某一点,只在该处修改。如果某个class经常因为不同的原因在不同的方向上发生改变,就需要警惕了。
散弹式修改
如果遇到某种情况,需要在不同的class内作出许多小的修改以响应之,需要修改的代码散布到四处,很难找到它们,也很容易忘记某个重要的修改。
依恋情结
无数次经验里,我们看到某个函数为了计算某值,从另一个对象中调用了几乎一半的取值函数。
方法:将这个函数移到另一个类中。判断那个class拥有最多【此函数使用】的数据,然后把这个函数和那些数据放在一起。
数据泥团
数据喜欢成群结队在一起。总是绑定在一起出现的数据应该放进属于它们自己的对象中。
- 找出这些数据的值域形式出现点,Extract Class将它们提炼到一个独立对象中。
- 对于函数签名,运用Introduce Parameter Object或者Preserve Whole Object为其减肥
基本类型偏执
放在一起的值域:运用Extract Class。
参数列表中基本类型数据:Introduce Parameter Object
array中挑选
数据:Replace Array with Object
switch惊悚现身
面向对象程序设计最明显的特征:少用switch(或case)语句
**面向对象中的多态概念可以为此提供优雅的解决办法
- 使用Extract Method将switch语句提炼到一个独立的函数中
- Move Method将它搬到需要多态性的class中
狎昵关系
有时你会看到两个classes过于亲密, 花费太多时间去探究彼此的private成分。
你可以采用 Move Method 和 Move Field 帮它们划清界线, 从而减少狎昵行径。
继承往往造成过度亲密,因为subclass对superclass的了解总是超过superclass的主观愿望。
过多的注释
常常会有这样的情况: 你看到一段代码有着长长的注释, 然后发现, 这些注释之所以存在乃是因为代码很糟糕。 这种情况的发生次数之多, 实 在令人吃惊
TIP: 当你感觉需要撰写注释, 请先尝试重构, 试着让所有注释都变得多余。