重构改善即有代码的设计

重构是软件开发中的一项重要技术,旨在不改变软件功能的前提下改进代码结构和设计。本文详细介绍了重构的原则,如何时进行重构,以及针对代码坏味道的识别与处理,如重复代码、过长函数和类等。同时,提供了多种重构技术,包括重新组织函数、在对象间搬移特性、重新组织数据和简化条件表达式等,以提高代码的可读性和维护性。
摘要由CSDN通过智能技术生成

(一)重构原则

1、何谓重构

对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

另一种解释是:使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

重构不止是代码整理,它提供了一种高效且受控的代码整理技术

2、为何重构

  • 改进软件设计:如果没有重构,程序的设计会逐渐变质,重构很像是在整理代码,你所做的就是让所有的东西回到应处的位置上。
  • 帮助找到bug:对代码进行重构,可以深入理解代码的作为,在搞清楚程序结构的同时,想不把bug揪出来都难。
  • 提高编程速度:良好的设计是快速开发的根本,改善设计、提高可读性,减少错误,这些都是提高质量。

3、何时重构

任何情况下我都反对专门拨出时间进行重构。重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地的进行。

三次法则

第一次做某件事情是只管去做;第二次做类似的事情会产生反感;第三次再做类似的事,你就应该重构

  • 最常见的重构时机是想给软件添加新特性的时候;

重构的另个一原动力是:代码的设计无法帮助我轻松的添加所需要的特性

  • 修改错误的时候
  • review代码的时重构

间接层和重构

计算机科学是这样一门科学:它相信所有的问题都可以通过增加一个间接层来解决。

大多数重构都为程序引入了更多的间接层,重构往往把大型的对象拆成多个小型的对象,把大型的函数拆成多个小型的函数。

但是,间接层是一把双刃剑。每次把一个东西分成两份,你就需要多管理一个东西。如果某个对象委托另一个对象,后者又委托另一个对象,程序会愈加难以阅读。

何时不该重构:有时候既有代码实在太混乱,重构它还不如重新写一个来得简单。

重写而非重构的一个清楚讯号是:现有代码根本不能正常运作。

(二)代码的坏味道

1、重复代码

如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合二为一,程序会变得更好 。

  1. 同一个类中有相同的表达式:提炼出重复的代码,然后让两个地方都调用被提炼出来的那一段代码;
  2. 两个互为兄弟的子类内含有相同的表达式:提炼出相同代码,将它推入超类内;
  3. 两个毫不相干的类中出现:将重复的代码提炼到一个独立的类中。

2、过长的类

拥有短函数的对象活得比较好、比较长。 间接层所能带来的全部利益——解释能力、共享能力、选择能力——都是由小型函数支持的。

每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立的函数中。

如何确定提炼哪一段代码?寻找注释是一个很好的技巧。它们通常能指出代码用途和实现手法之间的语义距离。如果代码前方有一行注释,就是提醒你:可以将这段代码替换成一个函数。

条件表达式和循环常常也是提炼的信号。

3、过大的类

如果想利用单个类做太多的事情,其内往往就会出现太多实力变量。

类内如果有太多代码,也是代码重复、混乱病最终走向死亡的源头。

4、过长参数列

太长的参数列难以理解,太多的参数会造成前后不一致、不容易使用,而且一旦你需要更多数据,就不得不修改它。如果将对象传递给函数,大多数修改都将没有必要。

###5、发散式变化

如果某个类经常因为不同的原因在不同的方向上发生变化,那么此时也许将这个对象分成两个会更好,这么一来每个对象就可以只因为一种变化而需要修改。

6、散弹式修改

如果没遇到某种变化,你都必须在许多不同的类内做出许多小修改,你所面临的坏味道就是散弹式修改。如果需要修改的代码散布四处,你不但很难找到它们,也很容易忘记某个重要的修改。

把所有需要修改的代码放进同一个类中,如果眼下没有合适的类可以安置这些代码就创造一个。

7、依恋情结

对象技术的要点在于:将数据和对数据的操作行为包装在一起

有一种经典的气味是:函数对某个类的兴趣高过对自己所处类的兴趣。某个函数为了计算某个值,从另一个对象那调用几乎半打的取值函数。

一个函数往往会用到几个类的功能,那么它该置于何处?我们的原则是:判断哪个类拥有最大被此函数使用的数据,然后就把这个函数和那些数据放在一起。

8、数据泥团

很多地方看到相同的三四项数据一起出现。这些总是绑在一起出现的数据应该拥有属于他们自己的对象。

首先找到这些数据以字段形式出现的地方,将它们提炼到一个独立的对象中。这么做的直接好处是可以将很多参数列缩短简化函数调用。

9、基本类型偏执

对象的一个极大价值在于:它们模糊了横旦与基本数据和体积较大的类之间的界限

对象技术的新手通常不愿意在小任务上运用小对象——结合数值和比重的money类、有一个起始值和一个结束值组成的range类。将原本单独存在的数值替换成对象,从而走出传统的洞窟,进入炙手可热的对象世界。

10、switch惊悚现身

面向对象的一个最明显的特征是:少用switch语句

一看到switch语句,就应该考虑以多态来替换它。

如果只是在单一函数中有些选择实例,且并不想改动它们,那么多态就有点杀鸡用牛刀了。

11、平行集成体系

每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

消除这种重复性的一般策略是:让一个继承体系的实例引用另一个继承体系的实例。

12、冗余类

某个类原本对得起自己的身价,但重构使它身形缩水,不再做那么多工作,这个时候请让这个类庄严赴义吧。

13、夸夸其谈未来性

企图以各种各样的钩子和特殊情况来处理一些非必要的事情,这种怀味道就出现了。如果用到了那就值得去做,如果用不到那就不值得,只会挡你的路,所以把它挪开吧。

如果你的某个抽象类其实没有起到太大的作用,函数上的某些参数未被使用…可以移除它们了。

14、令人迷惑的暂时字段

某个实例变量仅为某种特定的情况而设。这样的代码让人不易理解。在变量未被使用的情况下猜测当初其设置目的,会让你发疯的。

15、过度耦合消息链

如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另个一对象…这就是消息链。采用这种方式,意味着客户代码将与查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应的修改。

16、中间人

封装往往伴随着委托。你也许会看到某个类接口有一半的函数都委托给其他类,这样就是过度运用。

17、狎昵关系

有时会看到两个类过于亲密,话费太多的时间去探究彼此的private成分。过分狎昵的类必须拆散,帮它们划清界线,从而减少狎昵行径。

继承往往造成过度亲密,因为子类对超类的了解总是超过后者的主观愿望。如果你觉得该让孩子独立生活了,让他离开继承。

18、异曲同工的类

两个函数做同一件事,却有着不同的签名。

19、不完美的类库

类库函数构造的不够好,又不能修改它们:

  1. 如果只想修改类的一两个函数,可以引入外加函数。

  2. 如果想要添加一大堆额外行为,建立一个新类包含这些额外行为,让其成为子类。

20、纯稚的数据类

纯稚的数据类是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。

封装public字段;

恰当封装容器类字段;

移除不应修改的字段的设置函数;

提炼调用函数以隐藏取值/设值函数;

21、被拒绝的遗赠

子类只运用了父类的一部分函数和数据。 为子类建立一个兄弟类,将所有用不到的字段/函数下移至兄弟类,保证超类的纯粹;

22、过多的注释

注释之所以存在是因为代码很糟糕 。注释的最高境界——代码即注释。

当你感觉需要撰写注释时,请先尝试重构,试着让所有的注释都变得多余。

(三)重新组织函数

1、提炼函数

动机:看到一个过长的函数或者一段需要注释才能让人理解用途的代码,将这段代码放一个独立的函数中;

做法

  • 创造一个新函数,根据这个函数的意图来命名它;

    只要新函数的名称能够以更好的方式昭示代码意图,你也应该提炼它。但如果想不到一个更有意义的名称就别动

  • 将提炼的代码从原函数复制到新建的目标函数中;

  • 将被提炼代码段中需要读取的局部变量,当作参数传递给目标函数;

  • 在源函数中,将被提炼代码段替换为目标函数调用。

2、内联函数

一个函数的本体与名称同样清楚易懂。在函数调用点插入函数本体,然后移除该函数。

动机:

  • 一群组织不甚合理的函数。你可以将它们都内联到一个大函数中,再从中提炼出组织合理的小型函数。
  • 使用的太多的间接层,使得系统中的所有函数都似乎只是对另一个函数的简单委托,造成在委托动作之间晕头转向。

做法:

  • 检查函数,确定不具备多态;

    如果子类继承了这个函数,就不要将此函数内联,因为子类无法复写一个根本不存在的函数。

  • 找出这个函数的所有调用点;

  • 将这个函数的所有调用点都替换成函数本体。

3、内联临时变量

有一个临时变量,只被一个简单的表达是赋值一次,而它妨碍了其他重构手法。将所有对该变量的引用动作,替换为对它赋值的那个表达式自身

double basePrice = anOrder.basePrice();
return (base > 10000 );
<
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值