本系列是用来记录《重构,改善既有代码的设计》这本书的读书笔记。方便本身查看,也方便你们查阅。java
欲速则不达,欲达则欲速!算法
重构,绝对是写程序过程当中最重要的事之一。在写程序以前咱们不可能事先了解全部的需求,设计确定会有考虑不周的地方,并且随着项目需求的修改,也有可能原来的设计已经被改的面目全非了。更况且,咱们不多有机会从到到尾完成一个项目,基本上都是接手别人的代码,即便这个项目从头至尾参与,也有可能接手其它组员的代码。咱们都有这样的经验,看到别人的代码时感受就像屎同样,有一种强烈的想重写的冲动,但必定要压制住这种冲动,彻底重写,可能比原来好一点,但浪费时间不说,还有可能引入原来不存在的bug,并且,你不必定比原来设计的好,也许原来的设计考虑到了一些你没考虑到的状况。咱们写的代码,终有一天也会被别人接手,颇有可能到时别人会有和咱们如今同样的冲动。因此,咱们要作的重构,从小范围的重构开始。编程
重构不仅是能够改善既有的设计,还能够帮助咱们理解原来很难理解的流程。好比一个复杂的条件表达式,咱们可能须要好久才能明白这个表达式的做用,这时候,抽象出来,起一个易于理解的名字,函数名字很重要,下次再见到的时候,天然知道当初的想法了,好的代码赛过注释,毕竟注释有可能更新的不是很及时。c#
《重构,改善既有代码的设计》,这是一本经典之做,看过这本书要收获的是,让重构融入整个写代码的过程当中,让重构再也不做为一项独立的任务,而是在写代码的过程当中随时随地的进行,一个函数不容易理解,重构;添加新功能时很不方便,重构。数组

定义 |
对软件内部结构的一种调整,使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构,提升其可理解性,下降其修改为本。函数 |
为什么重构 |
- 重构改进软件设计:原来的设计不可能考虑到全部的状况,随意添加功能修改东西,可能已经看不出本来的设计了
- 重构使软件更容易理解
- 重构帮助找到bug:重构能够增长对代码的理解,从而容易发现bug
- 重构提升编程速度:重构虽然花费时间,可是重构能够改善程序的设计,使程序更不容易出现bug,使添加新特性更容易
|
什么时候重构 |
无须专门拨出时间进行重构,重构应该随时随地进行,事不过三,三则重构,当添加新功能时若是不是特别容易,能够经过重构使添加新特性更容易,修补错误时重构能够更容易发现bug,复审代码也是重构的好时机 |
代码的坏味道 |
- 重复代码
- 过长函数
- 过大的类
- 过长参数列
- 发散式变化:一个类受多种变化的影响
- 散弹式修改:一种变化引起多个类响应修改
- 依恋情结:函数对某个类的兴趣高过对本身所处类的兴趣,是时候考虑这个函数到底应该放在什么位置了
- 数据泥团:两个类中相同的字段,许多函数中相同的参数,这时候就可让他们拥有本身的类了,简而言之,相似的东西写一个类里
- 基本类型偏执:编写小对象,如表示范围的range
- switch惊悚现身:switch带来重复,一样的switch语句常常散布于不一样的地址,若是要加一个新的case子句,就必须找到全部switch语句并修改他们
- 平行继承体系:每当你为某个类增长一个子类,必须也为另外一个类增长一个子类,大多数时候你会发现,某个继承体系的类名前缀和另外一个继承体系的类名前缀彻底相同,是时候分离为两个继承体系了
- 冗赘类:没啥用的类就应该干掉
- 夸夸其谈的将来性:无用的抽象类,无用的预留参数
- 使人迷惑的局部变量
- 过分耦合的消息链:函数过大时,就应该提取函数
- 中间人:无用的委托,过多中间层
- 殊途同归的类:不一样的类或函数,干相同的事,写一个很差吗
- 过多的注释:若是一个函数须要过多的注释,是时候重构了,把代码当注释,好的名字就是注释
|
重构组织函数 |
- 提炼函数:代码粒度越小越容易重用,并且这部分代码被组织到一块儿后,能够起一个易于理解的名字解释其意图。要注意临时变量的处理
- 内联函数:对于一些函数,可能其实现和名字同样容易理解,不必再封装一层;或者一些不必的中间层均可以去掉,java中用final修饰,C++中用inline修饰,c#中没有内联函数,也许微软认为C++才须要关心性能,而C#关注快速开发,没必要理会这些开销吧。
- 内联临时变量:你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其它重构手法
- 以查询取代临时变量:应减小定义临时变量,如程序以一个临时变量保存某一表达式的运算结果并做为方法的返回值,直接用表达式做为返回结果就算了
- 引入解释性变量:有时候也会遇到很是复杂的表达式,定义一个临时变量,解释其用途也是能够的
- 分解临时变量
- 移除对参数的赋值:不要对函数传进来的参数赋值
- 创建新的类取代函数:有一个大型函数,这个函数有太多的临时变量,以致于不能重构,这时就能够另起炉灶,新建一个类,原来函数的参数就变成了新的类成员,能够方便的对新的勒种响应的函数进行各类重构。
- 替换算法:将两个方法中相同的部分,提取出来
|
在对象之间搬移特性 |
- 搬移函数:类中的一个函数使用另外一个类的对象的次数比使用本身所在类的对象的次数还要多,颇有可能这个函数定义错地方了
- 搬移字段
- 提炼类
- 内联类
- 隐藏委托关系:有时调用一个对象的方法时得到另外一个对象,而后再调用得到对象的一个方法得到另外一对象,如此反复,此时能够直接调用第一个对象再加一个方法直接返回最终对象
- 移除中间人
|
从新组织数据 |
- 自封装数据:为字段创建赋值取值函数
- 以对象取代数据值:有一个数据项,可能须要对这个数据项添加一个行为,把这个数据项封装为一个类
- 将值对象改成引用对象
- 将引用对象改成值对象:对象很差控制时改成不可变的值对象,这样就不用了考虑同步的问题了。
- 以对象取代数组:一个数组中的不一样元素表示不一样的东西,不容易理解
- 复制“被监视数据”:主要针对GUI中的数据,实现UI与逻辑分享,MVC,将V中的数据复制到M中,并在M中数据变化时经过listener进制或observer弄死同步更新UI
- 封装字段:public改成private,提供相应的访问函数
|
简化条件表达式 |
- 分解条件表达式:有一个复杂的条件表达式,将if条件,then、else中的三个段落所有提取出独立函数以方便理解
- 合并条件表达式
- 合并重复的条件判断:在条件表达式的每一个分支上有着相同的一段代码,把这段代码搬移到条件表达式以外
- 移除控制标记:不用经过控制标记来决定是否退出循环或跳过函数剩下的操做,直接break或return
- 以卫语句取代嵌套表达式:若是某个条件不常见,应该单独检查该条件,这种操做成为卫语句
- 以多态取代条件表达式
- 引入null对象
- 引入断言
|
简化函数调用 |
- 函数更名:好的函数名字很重要,名字起得好能够看出函数具体是作什么的,对于理解复杂逻辑很是有帮助
- 添加参数
- 移除参数
- 将查询函数和修改函数分离
- 令函数携带参数
- 以明确函数取代参数:有一个函数,其中彻底取决于参数值而采起不一样的行为,针对该参数的每个可能性,创建一个单独的函数
- 引入参数对象:某些参数老是同时出现,先建一个变量取代这些参数,减小参数的数量
- 移除赋值函数:若是类中的某个应该在对象建立时被赋值,此后再也不改变,不要添加赋值函数
- 隐藏函数:有一个函数历来没有被其它类用到,或者原本被用到,但随着类添加接口,以后就用不到了,那么隐藏这个函数,也就是减少做用域
- 封装向下转型:若是返回的值必定须要调用者转型,那么最好在函数中完成转型动做
- 以异常取代错误码
- 以测试取代异常:异常只应该被用于异常的、罕见的、意料以外的行为,不该该做为条件检查用
|
处理归纳关系 |
- 字段上移:连个子类拥有相同的字段,将该字段移至父类消除重复
- 函数上移:有些函数在各个子类中产生相同的结果,上移至父类消除重复并方便修改
- 构造函数本体上移
- 函数下移
- 字段下移
- 提炼子类
- 提炼父类:两个类有类似的特性,为这两个类创建一个父类,将相同特性移至父类
- 体链接口
- 折叠继承体系:父类与子类无太大区别,以前没考虑明白,消除继承关系,合并在一块儿
- 塑造模板函数
- 以委托取代继承:某个子类只使用父类接口中的一部分,将父类做为子类的一个字段,消除继承关系
- 以继承取代委托:一个类的行为基本上都是委托另外一个类,当另外一个类接口改变时也要同时修改委托类,直接继承省事方便
|
大型重构 |
- 梳理并分解继承体系:某个继承体系同时承担两项责任,创建两个继承体系,并经过委托关系让其中一个调用另外一个
- 将过程化设计转化为对象设计
- 将领域和表述/显示分离
- 提炼继承关系:某个类作了太多的事情,其中一部分工做是以大量条件表达式完成的,创建继承体系,以一个子类表示一种特殊状况
|