重构是在不改变软件可观察行为的前提下,对代码作出修改,以改进程序的内部结构。本质上说就是在代码写好后改进它的设计
重构往往意味着不了解软件行为下重构程序
2.在设计前期使用模式常常导致过度工程,单凭对完美的追求无法写出实用的代码,而"实用"是软件压倒一切的要素
《设计模式》指出:设计模式为重构提供类目标
重构可以反"软件会慢慢腐烂"现象,通过重构,设计不再是一切动作的前提,而是在整个软件开发过程中逐渐浮现出来
【29页】 如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达成目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。
【35页】 重构技术就是以微小的步伐修改程序。如果你犯下错误,很容易发现。
【37页】 任何一个傻瓜都可以写出计算机可以理解的代码,唯有写出人类容易理解的代码,才是优秀的程序员。
第二章:
重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
两顶帽子:使用重构技术开发软件时,你把时间分配给两种截然不同的行为:添加新功能,以及重构,添加新功能时只能添加新功能,不能修改已有代码;
重构时,只管改进程序结构,不能添加新功能
为何重构?1.重构改进软件设计[防止软件腐烂] 2.重构使软件更容易理解[给人理解] 3.重构提高编程速度
重构的时机:重构应该随时随地进行,你不能为了重构而重构,你之所以向重构,是因为你想做别的事情,而重构可以帮你把那件事做好
三次法则:第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构[事不过三,否则重构]
A添加功能时重构 B修补错误时重构 C代码复审时重构[二人代码重构,多人则设计重构]
《数据库重构》
不要过早发布接口,请修改你的代码所有权政策,使重构更顺畅。
【88页】何时不能重构
1.原有代码压根不能正常运行 2.项目接近最后期限,项目接近后期时重构,为时已晚
2.重构与设计,重构肩负一项特殊使命:重构与设计彼此互补
【kent,XP,极限编程】:重构可以取代预先设计。这意思是你根本不必做任何设计,只管按照最初想法开始编码,让代码有效运作,然后再将它重构
成型。事实上这种办法真的可行。
3重构与性能:我们猜测的性能瓶颈往往不是消耗性能的真正所在,只有通过测试才能找到真正影响性能的地方,并把时间用在真正需要优化的地方
第三章:"坏味道"<需要重构之处>
1.重复代码
2.过长的函数
3.过大的类[单个类做了很多的事情]
4.过长的参数列
5.发散式变化[一个类受多种变化的影响]
6.三弹式修改[一种变化引起多个类的修改]
7.依恋情节[一个类的某个函数使用的大部分数据来源于其他类]
8.数据泥团[两个类中相同的字段,许多函数签名中相同的参数]
9.基本类型偏执[或许你不愿意将一些基本数据类型转换为对象]【104页】
10.switch惊悚现身[多态替换switch]
11.平行继承体系[每当你为一个类增加一个子类的同时也需要为另一个类增加子类]
12.冗余的类[没什么实际作用的类]
13.夸夸其谈的未来性[为了未来变化而准备的接口]
14.令人迷惑的临时变量
15.过度耦合的消息链[一个对象传递了很远才得到处理]
16.中间人[一些类的大部分业务都是调用其他类实现的]
17.狎呢关系[过度亲密的类,如继承]
18.异曲同工的接口[功能类似的函数]
19.不完美的库类
20.数据类[简单包含字段和getter与setter方法的类]
21.被拒绝的馈赠[子类不想继承全部的函数]
22.过多的注释[尽量用表意的方法签名代替注释]【当你感觉需要撰写注释时,请先尝试重构,试着让所有注释多余】
第四章:构筑测试体系
4.1自测试代码的价值:xiug修复错误只需要很短的时间,但找到错误却需要很多的时间
【确保所有测试都完全自动化,让它们检查自己的测试结果】
【一套测试就是一个强大的bug侦测器,能够大大缩短查找bug所需要的时间】
【每当你收到bug,请先写一个单元测试来暴露bug】
4.3添加更多测试
【考虑可能出错的边界条件,把测试火力集中在那儿】[不要为每一个函数写测试,只为那些可能出错的地方写测试]
【不要因为测试无法捕捉所有的bug就不写测试,因为测试的确可以捕捉到大多数bug】
第五章:重构列表
5.2:寻找引用点[发射不能找到,继承覆盖不能找到]
第六章:重新组织函数
1.Extract Method-提炼函数:将代码放进一个独立函数中,并让函数名称解释该函数的用途
2.Inline Method-内联函数:在函数调用点插入函数主体,然后移除该函数
3.Inline Temp-内联临时变量:将所有对该变量的引用动作,替换为对他赋值的表达式自身
4.Replace Temp with Query-以查询取代临时变量:将临时变量换成相应的赋值方法
5.Introduce Explaining Variable-引入解释性变量:将复杂表达式的结果放进一个临时变量,以此变量名称来解释变量用途
6.Split Temporary Variable-分解临时变量:[有些临时变量被多次赋值],针对每次赋值都创造一个新符临时变量
7.Remove Assignments to Parameters-移除对参数的赋值:[方法修改了参数]以一个临时变量取代该参数的位置
8.Replace Method with Method Object-以函数对象取代函数:将这个函数放进一个单独的类中,如此一来局部变量就成了对象的字段。
然后你可以在同一个对象中将这个大型函数分解为多个小型函数
9.Substitute Algorithm-替换算法:将函数本体替换为另一种算法
第七章:在对象之间搬移特性
1.Move Method-搬移函数:[一个类的某个函数与另一个类交流更多,此时,该函数极有可能是放错地方了]
在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除
2.Move Field-搬移字段:[你的类的字段与另一个类更多交流,此时,这个字段是不是放错地方了]
在目标类新建一个字段,修改源字段的所有用户,令它们改用新字段
3.Extract Class-提炼类:[某个类做了应该由两个类做的事]
建立一个新类,将相关的字段和函数从旧类搬移到新类
4.Inline Class-将类内联化:将一个类的所有特性全部搬到另一个类中[这个类没干什么实事]
5.Hide Delegate-隐藏委托关系:在服务类上建立所需的所有函数,用以隐藏委托关系[最少知识原则,例如System.out.print()]
6.Remove Middle Man-移除中间人:某个类的大部分操作都是委托实现的,让调用者直接调用受托类
7.Introduce Foreign Method-引入外加函数:在客户类中建立一个函数,并以第一参数形式传入一个服务类实例。
[你需要为提供服务的类增加一个函数,但你无法修改这个类---不完美的库类]
第八章:重新组织数据
1.Self Encapsulate Field-自封装字段:为这个字段建立setter和getter函数[你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙]
2.Replace Data Value with Object-以对象取代数据值:将数据项变成对象[你有一个数据项,需要与其他数据和行为一起使用才有意义]
3.Change Value to Reference-将值对象改为引用对象:将值对象改为引用对象
4.Change Reference to Value-将引用对象改为值对象:将对象改为值对象[你有一个很小的对象,不可改变同时不易管理]
5.Replace Array with Object-以对象取代数组:以对象替换数组。对于数组中每一个元素,以一个字段表示[用数组表示不同意义的数据]
6.Duplicate Observed Data-复制"被监视数据":将该数据复制到一个领域对象中。建立一个Observed模式,用以同步领域对象和GUI对象内的重复数据
7.Change Unidirectional Association to Bidirectional-将单向关联改为双向关联
8.将双向关联改为单向关联:(尽量除去不需要的关联)
9.以字面量取代魔法数:常量的运用
10.Encapsulate Field-封装字段:getter和setter封装public字段
11.Encapsulate Collection-封装集合:(不要直接把集合从方法返回出去)
12.Replace Record with Data CLass-以数据类取代记录:类似于数据库实体
13.以类取代类型码:以一个新的类替换该数值类型码
14.以子类型取代类型码
15.以State/Strategy取代类型码
16.以字段取代子类:[子类只返回一些状态字段]
第九章:简化条件表达式
1.分解条件表达式:从if,else,then中分别提炼出独立函数
2.合并条件表达式:将相同处理情况的条件表达式合并,并用独立函数处理条件下的操作
3.合并重复的条件判断:所有条件下都会执行的代码片段应该合并并提取为独立函数
4.移除控制标记:用break,return等取代循环退出标记
5.以卫语句取代嵌套条件表达式:使用卫语句表现所有的特殊情况(卫语句:特殊检查)
6.以多态取代条件表达式
7.引入Null对象
8.引入断言
第十章:简化函数调用
1.Rename method-函数改名
2.Add Parameter-添加参数
3.Remove Paramter-移除参数
4.Separate Query from Modifer-将查询函数与修改函数分离
5.令函数携带参数:不同的函数功能相似,添加参数进行区别
6.以明确函数取代参数:针对参数的每一个可能值,建立一个独立函数
7.Preserve Whole Object-保持对象完整:参数改为整个对象[你需要对象中的某几个字段作为参数]
8.以函数取代参数
9.引入参数对象:某些参数总是同时出现
10.Remove Setting Method-移除设值参数
11.Hide Method-隐藏参数
12.以工厂函数取代构造函数
13.封装向下转型:不要让函数调用者转换函数函数的返回值
14.异常取代错误码
15.以测试取代异常
第十二章:处理概括关系
1.Pull Up Field-字段上移:将字段移至超类[两个子类拥有相同字段]
2.Pull Up Methid-函数上移:将函数移至超类[有些函数,在各个子类中产生完全相同的结果]
3.构造函数上移:在超类中新建一个构造函数,并在子类构造函数中调用它[各个子类的构造函数的本体几乎完全一样]
4.函数下移:将函数移到相关的子类去[超类中的某个函数只与部分(而非全部)子类有关]
5.字段下移:将这个字段移到需要它的那些子类去
6.提炼子类:[类中的某些特性只被部分实例用到]新建一个子类,将上面所说的那一部分特性移到子类中
7.提炼超类:[两个类有相似特性]为这两个类建立一个超类,将相同特性移至超类
8.提炼接口:[若干客户使用类接口中的同一子集,或者两个类的接口有部分相同]将相同的子集提炼到一个独立接口中
9.折叠继承关系:[超类和子类之间并无太大区别]将它们合为一体
10.塑造模板函数:[你有一些子类,其中相应的某些函数以相同顺序执行操作,但各个操作上的细节上有所不同]使用模板模式
11.委托取代继承:[某个子类只使用超类接口中的一部分,或者根本不需要继承而来的数据]子类中保存超类的引用,操作委托超类,取消继承关系
12.以继承取代委托:[你在两个类之间使用委托,并经常为整个接口编写许多极简单的委托函数]让委托类继承受托类
第十二章:大型重构
1.随时重构,而不是专门花时间做一些有风险的事
1.梳理并分解继承体系:[某个继承体系同时承担两项责任]建立两个继承体系,并通过委托关系让其中一个可以调用另一个
2.将过程化设计转化为对象设计:[你手上有一些传统过程化风格的代码]将数据记录变成对象,将大块的行为分为小块,并将行为移入相关对象中
3.将领域和表述/现实分离:[某些GUI类之中包含了领域逻辑]将领域逻辑分离出来,为它们建立独立的领域类
4.提炼继承体系:[你有某个类做了太多工作,其中一部分工作是以大量条件表达式完成的]建立继承体系,以一个子类表示一种特殊情况
第十五章:总结
1.随时挑一个目标进行重构
2.没把握就停下来
3.学习原路返回
来源:oschina
链接:https://my.oschina.net/u/207023/blog/550717