》重构:对软件内部的一种调整。目的是在不改变软件行为的前提下,提高其可理解性。降低修改成本。
》重构的第一步:为即将重构的代码建立一组测试环境。
》三次法则:事不过三,三则重构。
添加功能时候重构。
修补错误时重构。
》Duplicated code(重复代码)
如果在一个以上的地点看到相同的程序结构,合二为一,程序会更好。
》Long Method(过长函数)
1:如果代码的前方 有一行注释,就是在提醒你,可以将这段代码替换成一个函数。
2:条件表达式和循环也是提炼的信号。
》Large Class(过大的类)
如果想利用单个类做太多的事情。其内部往往会出现太多的实例变量。一旦如此。Duplicated Code也就接踵而至了。
》Long Parameter List(过长的参数)
》Divergent Change(发散式变化)
一个类受多种变化的影响
》Shotgun Surgevy(散弹式修改)
一个变化引发多个类进行修改。
》Feature Envy(依恋情节)
》Switch Statements(switch 惊悚现身)
用多态解决。
四:构筑测试体系。
》自测代码的价值
确保所有的测试都完全自动化,让他们检查自己的测试结果。
五:重构列表
》Extract Method(提炼函数)
你有一段代码可以被组织在一起并独立出来。
》Inline Method(内联函数)
在函数调用点插入函数本体,然后移除该函数。
》Inline Temp(内联临时变量)
你有一临时的变量,只被一个简单的表达式赋值一次,而他妨碍了其他重构的手法。
将所有对该变量的引用动作,替换为为他赋值的那个表达式本身。
》Replace Temp with Query(以查询取代临时变量)
你的程序以一个临时变量保存某一表达式的运算结果。
将这个表达式提炼到一个独立函数中。将这个临时变量的引用替换为新函数的调用。此后,新函数就可以被其他函数使用。
》Introduce Explaning Variable(引入解释性变量)
你有一个复杂的表达式,将该复杂表达式(或其中一部分)的结果放进一个临时的变量。以此变量名称来解释表达式的用途。
》Split Temporary Variable(分解临时变量)
你的程序有某个临时的变量被赋值超过一次。他既不是循环变量,也不用于搜集计算结果。
针对每次赋值,创造一个。对应的临时变量。
》Remove Assignments to Parameters(移除对参数的赋值)
代码对一个参数赋值
已一个临时变量取代该参数的位置。
》Replace Method with Method Object(以函数对象取代函数)
你有有一个大型的函数,其中对局部变量的使用使你无法采取使用Exaract method。
将这个函数放进一个单独的对象中,如此一来局部变量就成了对象内的字段。然后你可以在同一个对象中将这个大型函数分解成多个小函数。
》Substitute Algorithm(替换算法)
你要把摸个算法替换为另一个更清晰的算法。
以下图片仅供参考:
要点列表:
代码坏味:
简介:
坏味道 |
特征 |
情况及处理方式 |
目标 |
|
重复代码 |
1.重复的表达式 2.不同算法做相同的事 3.类似代码 |
同一个类的两个函数有相同表达式 |
重复代码提取为方法 |
相同表达式只在一个类的一个方法出现,供其他方法调用 |
兄弟类含有相同表达式 |
重复代码提取为方法 提升方法到父类 |
|||
不相干类含有相同代码 |
提取为独立类供调用 |
|||
过长函数 |
1.代码前面有注释 2.条件表达式 3.循环 |
|
提取方法 |
每个方法只做一件事,方法要定义完善、命名准确 |
过大的类 |
1.一个类中有太多实例变量 2.一个类中有太多代码 |
部分字段之间相关性高 |
相关的字段和方法提取为类 |
每个类负责一组具有内在的相互关联的任务 |
某些字段和方法只被某些实例用到 |
这些字段和方法移到子类中 |
|||
过长参数列 |
1.参数列过长 2.参数列变化频繁 |
方法可以通过其他方式获取该参数 |
让参数接受者自行获取该参数 |
只需要传给函数足够的、让其可以从中获取自己需要的东西就行了 |
同一对象的若干属性作为参数 |
在不使依赖恶化的情况下,使用整个对象作为参数 |
|||
被调用函数使用了另一个对象的很多属性 |
将方法移动到该对象中 |
|||
某些数据缺乏归属对象 |
首先创建对象 |
|||
发散式变化 |
一个类受多种变化的影响 |
类经常因为不同的原因在不同的方向上发生变化 |
将特定原因造成的所有变化提取为一个新类 |
针对某一外界变化的所有修改,只应发生在单一类中,而这个类中所有的内容都应反映此变化 |
散弹式修改 |
一种变化引发多个类的修改 |
某种变化需要在许多不同的类中做出小修改 |
把所有需要修改的代码放进同一个类中 |
针对某一外界变化的所有修改,只应发生在单一类中,而这个类中所有的内容都应反映此变化 |
依恋情结 |
一个函数使用其他类属性比使用自身类属性还要多 |
某个函数从另一个对象调用了几乎半打的取值函数 |
将依恋代码提取为单独方法,移动到另一对象 |
将数据和对数据的操作行为包装在一起 |
数据泥团 |
同时使用的相关数据并未以类的方式组织 1.两个类中相同的字段 2.许多函数中相同的参数 |
|
先将字段提取为类,再缩减函数签名中的参数 |
总是绑在一起的数据应该拥有属于它们自己的对象 |
基本类型偏执 |
过多使用基本类型 |
总是被放在一起的基本类型字段 |
提取类 |
将单独存在的数据值转换为对象 |
参数列中有基本类型 |
提取参数对象 |
|||
数组中容纳了不同的对象,需要从数组中挑选数据 |
用对象取代数组 |
|||
基本数据是类型码 |
使用类替换类型码 |
|||
带条件表达式的类型码 |
使用继承类替换类型码 |
|||
Switch语句 |
相同的switch、case语句散布于不同地方 |
根据类型码进行选择的switch |
使用多态替代switch |
避免到处做相同的修改 |
单一函数中有switch |
使用显式的方法取代参数 |
|||
平行继承体系 |
1.为某个类增加子类时,必须为另一个类增加子类 2.某个继承体系类名前缀和另一个继承体系类名前缀相同 |
|
一个继承体系中的实例引用另一个继承体系中的实例,然后迁移成员 |
避免到处做相同的修改 |
冗赘类 |
类无所事事 |
父类和子类无太大差别 |
将它们合为一体 |
|
某个类没有做太多事情 |
将这个类所有成员移到另一个类中,删除它 |
|||
夸夸其谈未来性 |
|
某个抽象类没有太大作用 |
将父子类合并 |
|
不必要的委托 |
将这个类所有成员移到另一个类中,删除它 |
|||
函数的某些参数未用上 |
移除参数 |
|||
函数名称带有多余的抽象意味 |
重命名函数名 |
|||
函数只被测试方法调用 |
连同测试代码一并删除 |
|||
令人迷惑的暂时字段 |
1.某个实例字段仅为某种情况而设 2.某些实例字段仅为某个函数的复杂算法少传参数而设 |
|
提取单独的类,封装相关代码 |
|
过度耦合的消息链 |
一长串的getThis或临时变量 |
客户类通过一个委托类来取得另一个对象 |
隐藏委托 |
消除耦合 |
中间人 |
某个类接口有大量的函数都委托给其他类,过度使用委托 |
有一半的函数 |
移除中间人 |
|
少数几个函数 |
直接调用 |
|||
中间人还有其他行为 |
让委托类继承受托类 |
|||
狎昵关系 |
某个类需要了解另一个类的私有成员 |
子类过分了解超类 |
将继承改为委托,把子类从继承体系移出 |
封装 |
类之间双向关联 |
去掉不必要的关联 |
|||
类之间有共同点 |
提取新类 |
|||
异曲同工的类 |
两个函数做同一件事,但是签名不同 |
|
合并 |
|
不完美的类库 |
类库函数构造的不够好,又不能修改它们 |
想修改一两个函数 |
在调用类增加函数 |
|
想添加一大堆额外行为 |
使用子类或包装类 |
|||
幼稚的数据类 |
某个类除了字段,就是字段访问器、设置器 |
|
1.用访问器取代public字段 2.恰当封装集合 3.移除不需要的设置器 4.搬移对访问器、设置器调用方法到此类 5.隐藏访问器、设置器 |
封装 |
被拒绝的馈赠 |
派生类仅使用了基类很少一部分成员函数 |
子类拒绝继承超类接口 |
使用委托替代继承 |
|
过多的注释 |
一段代码有着长长的注释 |
|
消除各种坏味道 |
|