重构技巧

重构

重构指在不改变程序原有行为的基础上,对既有代码进行修改,以改进其内部结构。

何时应该重构

添加功能时重构,修复bug时重构,代码评审时重构;

何时不应重构

既有代码太过混乱或不能正常运作,项目已近最后期限。

重构的好处

1. 重构可以改进软件设计

项目结束后,后期的bug修复、需求增加会导致代码逐渐腐败变质。冗余、结构混乱、难以理解、难以维护,难以扩展。如要修改某个错误,可能涉及到要修改的代码点很多。

软件开发中唯一保持不变的就是变化。当软件因为需求变更而开始逐渐退化时,运用软件重构改善我们的结构,使之重新适应软件需求的变化。经常性的重构可以维护代码原有的形态。

  1. 重构可以帮助理解代码

    在理解代码时,尝试去按自己的理解修改,使代码更趋简洁,随之而来的是看到一些以前看不到的设计层面的东西。

重构原则:小步快跑

敏捷软件开发的一个关键活动就是迭代,倡导进化式设计、增量开发。

在重构中也应采取相似的策略:小步快跑、进化式重构。每次修改一点点并测试,以保证不会引入bug。改动过大可能导致结果不可控,出现很多意想不到的bug。

代码坏味道

冗余代码、命名不规范、重复代码、过长函数、臃肿的类、参数过多、过度设计、过多的注释等等。

常用重构技巧(建议)

  1. 代码风格

    在已有代码上进行重构,重构后的代码应尽量与以前的好的代码风格(命名规则)保持一致。原来不好的风格,如变量跟运算符间未添加空格、函数参数之间空格、函数之间未空出一行等等,应进行修改。

  2. 删除冗余代码

    某个函数、类、变量如果不再工作,就应该将其删除。过多的冗余代码将会增加他人理解代码的难度,且使编译后的目标文件增大。局部变量应在使用前定义,没必要在函数入口处一次定义。

  3. 重命名

    模糊不清的方法名会影响代码的可用性。这些模糊不清的名称应该重命名为有意义且与业务有关的名称,来帮助更好地理解代码

    对类,接口,方法,变量、参数等重命名,以使得更易理解。名称应该能比较清晰的说明该函数、变量、类的职责。好的名称可以达到自注释的目的。类似于generalCall、setPriceAndWeight就不是很好的名字。

  4. 常量替换魔法数字

    对于有意义的并且到处被使用的魔法数字,应该使用常量替代。这能大大增强代码可读性和可理解性。

  5. 函数参数过多

    如果一个函数或方法的参数过多就会出现如果更改了其中一个参数, 就得在多个调用点进行更改。可以将多个参数封装成一个结构体或类。

  6. 分解臃肿函数、类承担的多个职责

    设计模式有一条原则叫做单一职责原则。也就是说函数、类、接口承担的职责应尽量单一。过多的职责会导致函数体、类方法过于庞大(几百上千行代码)、类职责过多影响代码阅读这是其一。其二可能存在重复代码,重复代码很多是通过代码拷贝实现,可能会导致错误扩散。其三多个职责就会存在多个变化点,在修改其中一 个职责时可能会影响其他代码,多个职责耦合性大,可能会引入错误。

在实际使用中,应尽量做到函数、接口、方法职责单一。类尽量做到职责单一。

  1. 过大的类拆分成多个类

    将一个类承担的多个职责拆分成多个类,以使每个类职责相对单一。

    方法:原有类中的方法和属性移动到新类。有时候一些类过于臃肿是因为它包含应该在其他类定义的方法。这些方法也应该被迁移到合适的类中。如负责Ui交互的功能与业务逻辑分开、数据库访问与业务逻辑拆分成不同的类,可以对数据库访问添加间接层,以兼容不同数据库的改变对业务逻辑的影响。

  2. 过长的方法

    将过长的函数应分解成多个命名良好的小函数。更容易理解且具有更好的复用性。很多程序员担心带来性能损耗,拆分后的多个小函数调用的性能消耗微乎其微。与其带来的好处相比可以忽略。如果确实导致性能损耗可以再通过重构改善性能。

  3. 消除重复代码

    重复代码会使目标文件体积增大。很多是通过代码拷贝实现,可能会导致错误扩散。

  4. 提取成函数。

    提取重复代码定义成独立函数,函数粒度小复用的机会就大。一旦需要修改仅需修改一处 ,职责单一容易理解。

  5. 提取方法

    若重复代码位于同一继承体系中,可以提取成基类的方法。

    若并不是完全重复,存在微小的差异,可以使用模板方法模式。

  6. 继承泛滥

    通过继承和组合都可以使一个类获得另一个类的功能。但使用继承时子类与父类是强依赖关系。在使用父类指针或引用的地方都可以使用子类替代父类。也就是说只有当两个类之间确实存在is-a关系时才能使用继承。强依赖关系使得子类父类耦合性很强。而组合相比较来说依赖减弱,当满足has-a关系时就可以通过组合来实现。

  7. 适当降低圈复杂度

    圈复杂度用来衡量一个模块判定结构的复杂程度,也可理解为覆盖所有的可能情况最少使用的测试用例数。复杂度高的代码判断逻辑复杂,可能会引入bug,且可读性很差。

    圈复杂度主要与分支语句(if、else、,switch 等)的个数有关。当一段代码中含有较多的分支语句,其逻辑复杂程度就会增加。有目的的降低核心类、核心方法的复杂度,可以降低软件的风险,增加软件的可扩展性。

  8. 简化条件表达式
  9. 合并条件表达式

    若一系列条件判断,得到相同结果,可以将这些测试合并为一个独立函数。

if(nHour <0 || nHour > 60)
return false;
if(nMinute  < 0 || nMinute  >  60)
return false;
if(nSec <0 || nSec > 60)
return false;
//其他代码

合并后:

if(false == isTimeValid(nHour, nMinute, nSec))
{
   return false;
}
//其他代码

bool  isTimeValid(int nHour,  int nMinute, int  nSec)
{
    if(nHour < 0 || nHour > 60 || nMinute  < 0 || nMinute  > 60 || nSec <0 || nSec > 60)
    {
     return false;
    }

    return true;
}
  1. 避免多层嵌套条件表达式

    条件表达式通常有两种形式: 所有分支都属于正常语句以及只有一个分支属于正常语句,其他都是非正常情况。 如果某个条件属于异常条件且不太常见,则应该单独检查该条件。嵌套导致代码可读性差,应尽量避免。

if(NULL != pBuf)
{
    if(false != func1())
    {
       if(false != Func2())
        {
          Func3();
        }
    }
}

修改后:

if(NULL == pBuf)
   return false;

if(false == func1())
   return false;

if(false == func2())
   return false;

func3();

没有了嵌套,结构更清晰易懂。

  1. 使用多态取代条件表达式(if else switch case)

    若在某个条件表达式中存在根据类型的不同具有不同的行为。可以考虑将原始函数声明为抽象函数,将条件表达式的每个分支放进一个子类重写的方法中。使用多态不必编写某些条件表达式并且若你想添加一种新类型,只需创建一个新的子类并重写该方法。这些更改对类的用户是透明的,上层不需要做任何更改。

  2. 避免过度超前设计

    过度设计是指代码的灵活性和复杂性超出了所需。代码应该满足当前的需求,并留有可扩展的余地。对于未来的变化,既不要考虑的太多,也不能一点都不考虑。刚开始的时候可能需求并不明确、或者我们对需求的理解还不甚明确,不能照顾到所有的变化,因为变化可能来自很多个方向。所有方向都考虑到将、过度的超前设计会导致我们的代码很复杂。在需求不甚明确时,很多超前的设计都是多余的。可以从中选择几个最可能发生变化的方向,使用设计模式来兼容这些可能的变化。后期随着我们对需求理解的不断加深,或是需求在某个方向上确实发生了变化,这时我们再回过头来通过重构来改进我们的设计,使其兼容此变化。

有一个比喻,就是说允许被来自同一个方向的同一把手枪的子弹击中一次。当你被击中之后,你应该意识到这个方向很危险,应该立即采取动作。如果反应迟钝,再次被击中就是不可原谅的。

推荐书籍:

《重构-改善既有代码的设计》

时间: 2024-10-06 11:34:35

重构技巧的相关文章

前端页面重构技巧总结TIP【持续更新...】

本文均为项目实战经验,要求兼容至IE8,所以以下内容均为兼容代码,欢迎各位小伙伴批评指教.其实重构页面是一门学问,看似简单,却暗藏很多学问.实际项目中页面的重构有以下几点最基本需求: 1.需要使用合理的标签进行语义化: 2.可扩展性,在页面的某个标签内增加新的内容(文字或标签),不会对原有内容造成影响. 3.当页面接受后台数据时,标签内容替换后,页面布局与样式不会受到影响. 4.兼容性(根据项目需要) 页面重构基本思想: 1.渐进增强思想(以兼容要求的最低版本为基础,主键向高层次的浏览器靠拢):

代码重构技巧方法

我是一个程序员.相信很多的程序员也有过同样的的经历,在公司一边骂别人的代码烂,一边写着比别人更烂的代码. 代码并不是一开始就变坏,代码变坏有以下可能 1.需求变更 2.维护代码的人的水平参差不齐 3.维护人员流动大 4.开发周期短,交付时间紧急 5.个人习惯 6.管理者对代码的要求 以上都是有可能使代码变坏的可能,那么如何写出好的代码?除了个人编程能力外还需要有一定的技巧,以下就是总结前人的经验 得来的,需要在编程中养成一种好习惯,让写好代码成为一种习惯吧!!!!! 1.使用卫语句 在什么情况下

最实用的10个重构小技巧排行榜

LZ最近一直在研究虚拟机源码,可惜目前还只是稍微有一点点头绪,无法与各位分享,庞大的JAVA虚拟机源码果然不是一朝一夕能搞定的,LZ汗颜. 本次我们抛开JAVA虚拟机源码这些相对底层的东西,LZ来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于LZ维护项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提

重构的技巧--cocoa china

我想一条童子军的军规:“始终保持露营地比你发现它的时候还要干净”.如果你在地上发现了一点脏东西,不管是谁弄的,都清理掉它.要为了下一拨来露营的人改善环境.(实际上,那条规矩的早期版本,出自Robert Stephenson Smyth Bden-Powell,童子军活动之父,说的是“努力使世界比你发现它时变得更好”.) 这就是为什么我不断的重构我的代码让它干净整洁. 当谈到编程的质量时,代码的可读性是我最关注的部分. 我想分享一些大部分人不知道的我用来简化代码的重构技巧. 字典的映射 假如你有一

重构的技巧

原文:http://www.cocoachina.com/industry/20140816/9397.html 我想一条童子军的军规:“始终保持露营地比你发现它的时候还要干净”.如果你在地上发现了一点脏东西,不管是谁弄的,都清理掉它.要为了下一拨来露营的人改善环境.(实际上,那条规矩的早期版本,出自Robert Stephenson Smyth Bden-Powell,童子军活动之父,说的是“努力使世界比你发现它时变得更好”.) 这就是为什么我不断的重构我的代码让它干净整洁. 当谈到编程的质量

最实用的代码重构小技巧排行榜TOP10

这次我们抛开JAVA虚拟机源码这些相对底层的东西,来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提到所有,LZ这里也只能提出一些平时会经常使用的一些手法,像一些比较高端的手法,各位有兴趣的可以去找一些专门的书籍涉猎. 本文部分重构小技巧可能主要与JA

什么是重构

     重构( Refactoring)就是在不改变 软件现有功能的基础上,通过调整 程序代码改善软件的质量.性能,使其程序的 设计模式和 架构更趋合理,提高软件的扩展性和维护性. 也许有人会问,为什么不在项目开始时多花些时间把设计做好,而要以后花时间来重构呢?要知道一个完 美得可以预见未来任何变化的设计,或一个灵活得可以容纳任何扩展的设计是不存在的.系统设计人员对即将着手的项目往往只能从大方向予以把控,而无法知道每 个细枝末节,其次永远不变的就是变化,提出 需求的用户往往要在软件成型后,始才

重构培训有感

重构,对于一个工作刚刚满一年的我来说,本是一个陌生的概念.但好在,在参加本次培训之前,项目组内部已经开始对重构提起了重视,我也有幸读了<重构,改善代码的既有设计>这本书.其实,看过这本书的人可能都会有一个感觉,就是书中所概括的种种我们都知道,但是我们是否在意过这种细节,是否约束过自己,每个人心里都有一个答案吧.通过培训,还是对重构有了一个整体的认识. 我想,在重构之前,我们要明白为什么要对代码或者说软件进行重构.之所以要重构,是因为我们的软件中出现了这样或者那样的坏味道,具体的坏味道这里不做赘

Xcode 工具 小技巧

1. 快速打开辅助界面 快捷键:使用Option + 单击文件 2. 辅助编辑器更多打开方式 快捷键: Option+shift +单击文件 3. tab页面快捷键 快捷键: Command+T 新建tab Command+W 关闭tab Command +}/{ 左右切换tab 4. 更改双击为打开一个新tab 5. Xcode快照功能snapshot 快照就是整个代码的存档拷贝.多数用于重构之前的保存操作. 创建快照:Command + Control + S 快照管理 (删除.导出 快照)