代码重构

读书笔记
一.重构的定义
(名词定义)对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
(动词定义)使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构
二.重构时注意事项
1.可适当修改变量名称,很有必要
2.重构之中还可以包含另一个重构
3.重构时最好小步进行,这样犯错几率最小
4.绝大多数情况下,函数应该放在它所使用的数据的所属对象内
5.最好不要在另一个对象的属性基础上使用switch语句。如果不得不使用,也应该在自己的数据上使用,而不是在别人的数据上使用。
6.重构之前代码必须起码能够在大部分情况下正常运作
7.确保所有测试都完全自动化,让他们检查自己的测试结果
8.一套测试就是一个强大的bug侦测器,能够大大缩减查找bug所需要的时间。
9.一个函数的本体与名称同样清楚易懂
10. java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。 instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
11.魔法数:拥有特殊意义,却又不能明确表现出这种意义的数字。
12.“卫语句”:在条件表达式中,如果两条分支都是正常行为,就应该使用形如if····else ··的条件表达式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数返回。这样的单独检查常常被称为“卫语句”。
13.重构之前,首先检查自己是否有一套可靠地测试机制,这些测试必须有自我检验能力。
三.重构的作用
1.改进软件设计
2.使软件更容易理解
3.帮助找到bug
4.提高编程速度
四.何时需要重构
1.添加功能时重构
2.修补错误时重构
3.复审代码时重构
五.何时不该重构
1.需要重新编写所有代码(不能进行重构时的修改方法:将大型软件重构为封装良好的小型组件,逐一对组件做出重构或者重建的决定)
2.项目已近最后期限,尽量避免重构

六.重构的难题
1.数据库(两方面的原因:a.程序与数据库结构紧密耦合在一起b.数据迁移)
2.修改接口(方法:保留旧接口,刚好的方法不要发布接口,Java中的特别的接口修改:在throws子句中增加一个异常)
七.编写快速软件的三种方法
1.时间预算法(通常用于性能要求极高的实时系统)
2.持续关注法(要求任何程序员在任何时间做任何事时,都要设法保持系统的高性能)
3.利用90%的统计数据(首先编写构造良好的程序,不对性能投以特别的关注,一直到性能优化阶段,此时首先用一个度量工具监控程序的运行,找出性能热点所在的一小段代码,然后集中对这一小段代码进行优化)
八.哪些部分可以重构
1.重复代码
A.同一个类的两个函数含有相同的表达式
B.两个互为兄弟的子类内含相同表达式
C.两个毫不相干的类出现重复代码
2.过长函数
3.过大的类
4.过长参数列
5.发散式变化(一个类受多种变化的影响)
6.散弹式修改(一种变化引发多个类相应修改)
7.依恋情结(对象技术的全部要点在于将数据和对数据的操作行为包装在一起)
8.数据泥团(两个类中相同的字段,许多函数签名中相同的参数,这些绑在一起出现的数据应该拥有属于他们自己的对象)
9.基本类型偏执(如果只为做一两件事二创建结构类型显得太麻烦)
10.Switch惊悚现身(面向对象中的多态可以解决)
11.平行继承体系(每当为某个类增加一个子类,必须为另一个类相应增加一个子类)
12.冗赘类(一个类的所得不知其身价就让他消失)
13.夸夸其谈未来性
14.令人迷惑的暂时字段
15.过度耦合的消息链(如果看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再向后者请求另一个对象,然后在~~一旦对象间的关系发生任何变化,客户端就不得不做出相应修改)
16.中间人(委托不应该过多使用委托)
17.狎昵关系(不应该使类过度亲密)
18.异曲同工的类
19.不完美的库类
20.纯稚的数据类
21.被拒绝的遗赠(子类应该继承超类的函数和数据,但是如果他们不想或者不需要继承)
22.过多的注释
九.关于测试
测试的重要技巧是寻找边界条件
在断言中加入一条消息,如果测试失败,这条信息就会显示出来
当事情被认为应该出错时,别忘了检查是否抛出了预期的异常
任何测试都不能证明一个程序没有bug
把测试集中在最可能出错的地方
十.重构方法(重点/难点)
重新组织函数(6)
重构方法 症结所在 解决方案
提炼函数 一段代码可以被组织在一起并被提炼出来 将这段代码放在一个独立函数中,并让函数名称解释该函数的用途
内联函数 一个函数的本体和名称同样清楚易懂 在函数调用点插入函数本体,然后移除该函数
内联临时变量 有一个临时变量,只被一个简单表达式赋值一次,而他妨碍了其他重构手法 将所有对该变量的引用动作,替换为对他赋值的那个表达式自身
以查询取代临时变量 以一个临时变量保存某一表达式的运算结果 将表达式提炼到独立函数中,将这个临时变量的所有引用点替换为对新函数的调用。
引入解释性变量 有一个复杂的表达式 将该复杂表达式的结果放在一个临时变量,以此变量名称来解释表达式用途
分解临时变量 程序中有某个临时变量被赋值吵过一次,它既不是循环变量,也不被用于收集计算结果 针对每次赋值,创建一个独立,对应的临时变量
移除对参数的赋值 代码对一个参数进行赋值 以一个临时变量取代该参数的位置
以函数对象取代函数 有一个大型函数,其中对局部变量的使用使我们无法采用提炼函数 将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段,然后可以再同一个对象中将这个大型函数分解为多个小型函数
替换算法 把某个算法提炼为另一个更为清晰的算法 将函数本体替换为另一个算法
在对象之间搬移特性(7)
搬移函数 程序中有个函数与其所驻类之外的另一个类进行更多交流,调用后者或者被后者调用 在该函数最常引用的类中建立一个有着类似行为的新函数,将旧函数变成一个单纯的委托函数,或是将就函数完全移除
搬移字段 程序中某个字段被其所驻类之外的另一个类更多的用到 在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段
提炼类 某个类做了应该由两个类做的事 建立一个新类,将相关的字段和函数从旧类搬移到新类
将类内联化 某个类没有做太多的事情 将这个类的所有特性搬移到另一个类中,然后移除原类
隐藏委托关系 客户通过一个委托类来调用另一个对象 在服务类上建立客户所需要的所有函数,用以隐藏所有委托关系
移除中间人 某个类做了过多的简单委托动作 客户直接调用受委托类
引入外加函数 我们需要为提供服务的类增加一个函数,但我们无法修改这个类 在客户类建立一个函数,并以第一参数形式传入一个服务类实例
引入本地扩展 需要为服务类提供一些额外函数,但我们无法修改这个类 建立一个新类,是它包含这些额外函数,让这个扩展品成为源类的子类或包装类
重新组织数据(8)
自封装字段 我们直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙 为这个字段建立取值/设值函数,并且只以这些函数来访问字段
以对象取代数据值 有一个数据项,需要与其他数据和行为一起使用才有意义 将数据项变成对象
将值对象改为引用对象 从一个类衍生出许多彼此相等的实例,希望将他们替换为同一个对象 将这个值对象变成引用对象
将引用对象改为值对象 有一个引用对象,很小且不可变,而且不易管理 将它变成一个值对象
以对象取代数组 有一个数组,其中的元素各自代表不同的东西 以对象替换数组,对于数组中的每个元素,以一个字段来表示
复制”被监视数据” 有一些数据置身在GUI空间中,领域函数需要访问这些函数 将改数据复制到一个领域对象中,建立一个observer模式,用以同步领域对象和GUI对象内的重复数据
将单向关联改为双向关联 两个类都需要使用对方特性,但其间只有一条单向连接 添加一个反向指针,并使修改函数能够同时更新两条连接
以字面常量取代魔法数 有一个字面数值带有特殊含义 创造一个常亮,根据其意义为他命名,并将上述的字面数值替换为这个常量。
封装字段 类中存在一个public字段 将它声明为private,并提供相应的访问函数
封装集合 有个函数返回一个集合 让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数
以数据类取代记录 需要面对传统编程结构中的记录结构 为该记录创建一个“哑”数据
以类取代类型码 类之中有一个数值类型码,但它不影响类的行为 以一个新的类替换该数值码函数
以子类取代类型码 有一个不可变的类型码,它会影响类的行为 以子类取代这个类型码
以State/Strategy取代类型码 有一个类型码,它会影响类的行为,但是无法通过继承手法消除它 以状态对象取代类型码
以字段取代子类 各个子类的唯一差别只在“返回常量数据”的函数身上 修改这些函数,使他们返回超类的某个(新增)字段,然后销毁子类
简化条件表达式(9)
分解条件表达式 有一个复杂的(if-then-else)语句 从if,then,else三个段落中分别提炼出独立函数
合并条件表达式 有一系列条件测试,都得到相同结果 将这些测试合并为一个条件表达式,并将这个表达式提炼为一个独立函数
合并重复的条件片段 在条件表达式的每个分枝上有着相同的一段代码 将这段重复代码搬移到条件表达式之外
移除控制标记 在一系列布尔表达式中,某个变量带有“控制标记”的作用 以break或return语句取代控制标记
以卫语句取代嵌套条件表达式 函数中的条件逻辑使人难以看清正常的执行路径 使用卫语句表现所有特殊情况
以多态取代条件表达式 有个条件表达式,根据对象类型的不同而选择不同的行为 将这个表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数
引入null对象 需要再三检查某对象为null 将null值替换为null对象
引入断言 一段代码需要对程序状态做出某种假设 以断言明确表现这种假设
简化函数调用(十)
函数改名 函数的名称未能揭示函数的用途 修改函数名称
添加参数 某个函数需要从调用段得到更多消息 为此函数添加一个对象参数,让该对象带进函数所需消息
移除函数 函数本体不再需要某个参数 将该参数去除
将查询函数和修改函数分离 某个函数既返回对象状态值,又修改对象状态 建立两个不同的函数,其中一个负责查询,另一个负责修改
让函数携带参数 若干函数做了类似的工作,但在函数本体中却包含了不同的值 建立单一函数,以参数表达那些不同的值
以明确函数取代参数 有一个函数,其中完全取决于参数值而采取不同行为 针对该参数的每一个可能值,建立一个独立函数
保持对象完整 从某个对象中取出若干值,将他们作为某一次函数调用时的参数 改为调用整个对象
以函数取代参数 对象调用某个函数,并将所得结果作为参数,传递给另一个函数,二接受该参数的函数本身也能够调用前一个函数 让参数接受者去除该项参数,并直接调用前一个函数
引入参数对象 某些参数总是很自然的同时出现 以一个对象取代这些参数
移除设值函数 类中的某个字段应该在对象创建时被设值,然后就不在改变 去掉该字段的所有设值函数
隐藏函数 有一个函数,从来没有被其他任何类用到 将这个函数修改为private
以工厂函数取代构造函数 希望在创建对象时不仅仅是在做简单的建构工作 将构造含糊替换为工厂函数
封装向下转型 某个函数返回的对象,需要由函数调用者执行向下转型 将向下转型动作移到函数中
以异常取代错误码 某个函数返回一个特定的代码,用以表示某种错误情况 改用异常
以测试取代异常 面对一个调用者可以预先检查的条件,你抛出了一个异常 修改调用者,使他在调用函数之前先做检查
处理概括关系(十一)
字段上移 两个子类拥有相同的字段 将该字段移至超类
函数上移 有些函数,在各个子类中产生完全相同的结果 将该函数移至超类
构造函数本体上移 在各个子类中拥有一些构造函数,他们的本体几乎完全一致 在超类中新建一个构造函数,并在子类构造函数中调用它
函数下移 超类中的某个函数只与部分子类有关 将这个函数移到相关的那些子类中去
字段下移 超类中的某个字段只被部分子类用到 将这个字段移到需要他的那些子类中去
提炼子类 类中的某些特性只被某些实例用到 新建一个子类,将上面所说的那一部分特性移到子类中
提炼超类 两个类有相似特性 为这两个类建立一个超类,将相同特性移至超类
提炼接口 若干客户使用类接口中的同一子类,或者两个类的接口有部分相同 将相同的子类提炼到一个独立接口中
折叠继承关系 超类和子类之间无太大区别 将他们合为一体
塑造模板函数 有一些子类,其中相应的某些函数以相同顺序执行相似的操作,但各个操作的细节上有所不同 将这些操作分别放进独立函数中,并保持他们都有相同的签名,浴室原函数也就变得相同了,然后将原函数移至超类
以委托取代继承 某个子类只使用超类接口的一部分,或是跟布恩不需要继承而来的数据 在子类中新建一个字段永一堡村超类,太哦正子类函数,令他改而委托超类,然后去掉两者之间的继承关系
以继承取代委托 在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托关系 让为拖累继承受托类
大型重构
梳理并分解继承体系 某个继承体系同时承担两项责任 建立两个继承体系,并通过委托关系让其中一个可以调用另一个
将过程设计转化为对象设计 有一些传统过程化风格的代码 将数据记录变成对象,将大块的行为分成小块,并将行为移入相关对象中
将领域和表述/显示分离 某些GUI类之中包含了领域逻辑 将领域逻辑分离出来,为他们建立独立的领域类
提炼继承体系 有某个类做了太多工作,其中一部分工作是以大量条件表达式完成的 建立继承体系,以一个子类表示一种特殊情况

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开发小牛牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值