什么是重构?在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种经过千锤百炼形成的有条不紊的程序整理方法,可以最大限度地减少整理过程中引入错误的几率。本质上说,重构就是在代码写好之后改进原来的设计,是对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
为什么要重构?软件的开发都是基于良好的设计基础之上进行的。但是随着时间的流逝,代码不断地被修改,修改代码的人员不断变化,一个良好设计的系统也会逐渐衰弱,代码质量、结构也逐渐沉沦。需要使用重构的手段长期保证代码的质量及结构,重构的目的是改进软件的设计、使软件更容易理解、定位Bug更方便、提高编程的速度。
何时重构?- 增加新功能的时候,发现需要重构来便于新功能的添加。
- 消除重复代码的时候,例如你的代码中有三个地方都有10几行同样的代码,这个时候,就可以把这10几行代码单独封装成一个独立的函数供其他方法进行调用。
- 修复bug的时候,你觉得这个bug产生的原因是由于代码的组织逻辑不当,这个时候就可以把这一块进行重构。
- 代码评审的时候,很多人对代码都提出了修改的建议,这个时候可以进行重构。
当然,重构也不是随心所欲,相对于重构本身,更重要的是要养成重构意识,这样当你遇到不好的代码时,就能利用重构的手段来消除这些坏代码代码带来的隐患。另外,重构的前提,你必须对代码有着足够的了解,并且要能评估重构后对整个系统的影响和风险。
如何重构?- 提炼函数。完全一样的代码使用超过一次以上;可以组织到一个独立函数;
- 内联函数。一个函数的函数体与函数名一样的简单易懂;使用函数体替换函数;
- 内联临时变量。有一个临时变量只被赋值一次后使用;可以在使用的地方替换为赋值语句;
- 以查询取代临时变量。使用一个变量记录一个复杂的表达式后使用;可以将复杂表达式替换为函数后使用。
- 引入解释性变量。复杂的条件表达式难以理解;分解表达式用多个临时变量记录并重新组织;
- 分解临时变量。有一个临时变量被多次赋值;每次赋值使用一个独立变量;
- 移除对参数的赋值。函数内部对入参有赋值操作;如此使用final,函数内使用临时变量接收;
- 以函数对象取代函数。函数过大难以分解;根据函数用意新建一个类,参数变为成员继续进行分解;
- 自封装字段。类中函数直接访问字段;提供取值/设值方法,调用方法使用字段;
- 以对象取代数据值。有一项数据和其他数据和行为一起使用才有意义;数据项变成对象;
- 将单向关联改为双向关联。两个类相互使用对方特性;添加反向指针,修改函数同时更新两条链接;
- 将双向关联改为单向关联。两个类相互关联关系,一个类不再关心另一个类的特性;去除不必要的关联;
- 以对象取代数组。有一个数组,元素代表不同的东西;以对象替换数字,数字元素以字段代替;
- 以常量替代魔法数。魔法数又称魔鬼数,有特殊意义却不能表达意义的数字;用常量替代;
- 封装字段。类中存在public字段;修改字段为private,提供相应的访问函数;
- 封装集合。函数返回一个集合;返回集合只读副本,提供添加/移除元素的函数;
- 分解条件表达式。条件表达式if,else中内容复杂;将复杂的内容放在函数中进行调用;
- 合并条件表达式。多个条件内部执行内容相同;多个条件放在函数中,判断函数进行操作。
- 合并重复的条件片段。条件表达式中有相同的代码或者函数;相同的代码或函数搬移到表达式外;
- 移除控制标记。条件表达式中用变量做控制标记;使用break,return,continue替换控制标记;
- 以卫语句取代嵌套条件表达式。某个条件极其罕见,条件表达式中应该单独检查,并且条件成立时立刻从函数中返回。这样单独检查常被成为“卫语句”。
- 使用if,else组织复杂的条件表达式;应使用卫语句表现所有特殊情况;
- 以多态取代条件表达式。条件表达式中根据对象类型选择不同的行为;建立继承体系,原始函数声明为抽象函数,子类进行覆盖;
- 函数改名。名不达意;修改函数名,更确切的表达函数作用;
- 添加参数。函数需要获取调用方更多信息;将参数封装,传递对象给函数;
- 移除参数。函数入参,部分参数不再使用;剔除多余参数;
- 将查询函数与修改函数分离。有些函数既具有查询能力又具有修改能力;一分为二,单一职责;
- 令函数携带参数。多个函数功能类似,某个取值影响行为;合并函数,参数控制差异取值;
- 以函数取代参数。临时变量记录函数返回,再传递给函数;调用函数的参数使用函数;
- 保持对象完整。从对象中取出若干值作为函数参数;直接将对象作为参数
- 令明确函数取代参数差异。函数内部根据参数不同有不同的反应;每一个参数值提取为一个函数;
- 移除设置函数。成员变量在对象实例化时设值,之后在不变化;删除设值函数;
- 隐藏成员函数。public方法没有被其他类调用;修改方法修饰符为private;
- 引入参数对象。某些参数总是一起发生改变;变化的参数封装起来,以对象传递;
- 封装[向下转型]动作。函数返回对象后,调用者需要向下转向;函数内部转型后返回;
- 搬移函数。类中某个方法与另外一个类交流很多;将此函数迁移到另外一个类中;
- 搬移字段。类中某些字段另外一个类关注更多;将这些字段迁移到另外一个类中;
- 提炼类。类中有些字段总是一块变化(数据泥团);字段及相关方法提取到一个类;
- 将类内联化。某个类能力很有限;字段方法迁移到其他类,删除此类;
- 隐藏委托关系。通过委托类调用另一个对象;服务类中建立客户所需函数,隐藏委托关系。
- 移除中间人。某个类做了过多的简单委托动作;直接调用委托类;
- 字段上移。两个子类拥有相同的字段;将该字段移至超类;
- 函数上移。有些函数在各子类产生的结果完全相同;将该函数移至超类;
- 字段下移。超类中某个字段只被部分子类用到;将此字段移到需要它的子类中;
- 函数下移。超类中某个函数只与部分子类有关;将此函数移到相关的子类中;
- 构造函数本体上移。各个子类中拥有一些构造函数,内容差异不大;超类中新增构造函数;
- 提炼子类。类中的某些特性只被某些实例用到;将这些特性迁移到一个子类中;
- 提炼超类。两个类有相似特性;为这两个类建立一个超类,相同特性移至超类;
- 折叠继承体系。超类和子类没有太大差异;将他们合并为一个类;
总结了解这么多的重构方法,如何开始学习?先挑一个目标。从发臭的代码入手。没有把握就停下来。在向目标前进过程中,遇到不确定的就可以停下来。学会原路返回。小步快走,多测试,一旦失败即可回退,重新再来。协作重构。和别人一起进行,相互检查,同步推进。