第二章、重构的原则

2.1 何为重构

名词定义:

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

动词定义:

  • 使用一系列重构手法,在不改变软件壳观察行为的前提下,调整其结构

重构只是整理代码?

  • 重构可以理解为整理代码,但它可以提供一种更高效且受控的代码整理技术

两顶帽子:

  • 分为:添加新功能、重构
  • 重构的时候就不要在添加新的功能了

2.2 为何重构

1. 重构改进软件设计

  • 如果没有重构,程序的设计会逐渐变得腐败变质。也就是说,代码的流失是累积性的
  • 改进的一个重要方向就是消除重复代码(DRY)

2. 重构使软件更容易理解

  • 代码的理解是面向人类的
  • 作者对与那些能够立刻查阅的东西,大多都不刻意区记忆。往往在查询的过程中,你就逐渐地记住了
  • 利用重构来理解不熟悉的代码
  • 重构使得代码变得更加地清晰,可以让你发现一些以前看不到的设计层面的东西

3. 重构帮助找到BUG

  • 也就是重构能够提高对代码的理解,也就能够发现原先代码的一些不足之处
  • 优秀的习惯能够让你成为一个好程序员

4. 重构提高编程速度

  • 重构帮助你更快速地开发程序
  • 良好的设计是快速开发的根本,因为其降低了程序的修改成本和理解成本(在大型程序中往往更容易发现)

2.3 何时重构

  • 重构不来九不是一件应该特别拨出时间来做的事情,重构应该随时进行

三次法则

  • 重复三次做同样的事情,就应该着手重构

添加功能时重构

  • 最常见的重构时机,就是添加新的功能的时候
  • 即降低软件的修改成本,提高对代码的理解。在未来添加新的功能可以更加的容易和快速

修补错误时重构

  • 调试的时候运用重构,多半是为了让代码更具有可读性

复审代码时重构

  • 很多公司都会做常规的代码复审,因为这种活动可以改善开发的状况
  • 让知识在开发团队里面传播,也能够想出更好的点子
  • 重构一样可以帮助你复审别人的代码
  • 在复审的时候,最好是复审者搭配一个原作者,也就是极限编程里面所说的“结对编程”

为什么重构有用?

  • 程序有两面价值:“今天可以为你做什么”和“明天可以为你做什么”
  • 重构也就是为未来软件的修改降低成本

程序难以相与的原因

  1. 难以阅读的程序,难以修改
  2. 逻辑重复的程序,难以修改
  3. 添加新的功能时,需要修改已有代码的程序,难以修改
  4. 带复杂条件逻辑的程序,难以修改

我们期望程序

  1. 容易阅读
  2. 所有逻辑都只在唯一地点指定
  3. 新的改动不会危及现有的行为
  4. 斤可能简单表达条件逻辑

2.4 怎么对经历说

  • 对于懂技术的经历来说,解释重构应该不难
  • 对于值关心质量的经理,那么问题九集中在了“质量”上面
  • 大量研究结果表明,技术复审是减少错误、提高开发速度的一条重要的途径
  • 或者在难以解释的情况下,不告诉可能比较好

间接层和重构

  • 计算机科学是这样一门科学:他相信所有的问题都可以添加一个间接层来解决
  • 但是间接层是一把双刃剑,每次把东西分成两份,你就要多管理一份东西,代理委托就回变得更加复杂

间接层的价值:

  1. 允许逻辑共享(重复函数)
  2. 分开解释意图和实现
  3. 隔离变化(比如子类,可以避免影响父类的操作(有两个地方调用了父类,我就可以考虑设计一个子类))
  4. 封装条件逻辑(对象的奇妙机制:多态消息,可以清晰灵活地表达条件逻辑,将条件逻辑转换为消息形式,往往能够降低代码的重复、增加清晰度并提高弹性)

2.5 重构的难题

  • 学习一种可以大幅度提高生产力的新技术时,你总是难以察觉其不适用的场合
  • 在10年前,对象技术也往往如此。要反对那种盲目性
  • 随着对重构的了解日益增多,我们也要监控那些重构可能引入的问题

数据库

  • 重构经常出现的一个领域就是数据库,绝大多数的商用程序和数据库都是紧密地耦合在一起,数据库结构和对象模型紧密的结合在一起
  • 还有一个问题就是数据库迁移(migration),数据结构的改变导致你需要迁移所有的数据
  • 非对象数据库中:往往可以引入一个中间层来解决问题:分离两个模型之间的变化,这样只要修改中间层即可,虽然这样会增加系统的复可以杂度,但是可以带给你很大的灵活度
  • 无需在一开始的时候就插入间接层,在需要修改的时候,进行修改就好
  • 某些面向对象的数据库,可以提供不同版本的自动迁移功能,减少数据迁移时的工作量。需要注意数据结构的变化,以及修改数据的访问方法

修改接口

  • 如果所有的接口影响的信息都在你的掌握之下,那么修改不成问题
  • 问题在于对于那些已经发布了的接口,你需要考虑存修改接口所带来的问题
  • 所以,一般上我们新的接口都需要兼容旧的接口。直到所有的用户把迁移到新的接口里
  • 另一种是,不要过早发布你的接口。或者可以在旧的接口中产生一个警告,然红会尽快地迁移到新的接口

难以通过重构手法完成的设计改动

  • 在某些情况下,我们可以有效地进行重构,对于安全性的问题,常常遇到
  • 所以,我们常常在重构之前,进行预想重构的方法,如果重构足够简单,那么没有问题,可以直接重构

何时不该重构

  • 代码过于混乱,重构的难度比直接重写要高
  • 重构之前,代码必须要能够正常运作
  • 一个折中的办法:将“大块头软件”重构为封装良好的小型组件。然后就可以逐一对组件进行重构或重建
  • 项目时间接近最后期限,你应该避免重构

2.6 重构与设计

重构肩负着一项特殊的使命:它和设计互补

  • 初识编程的时候,埋头写代码,混混噩噩地进行开发。发现,事先做好设计可以节省返工的高昂成本
  • 也有另一种观点,重构可以取代设计,不断的重构可以获得良好的设计软件
  • 我的看法:无论是先设计后开发,还是后期进行重构。都表明开发人员要有一定的软件设计能力,因为我们无法避免地会发现程序中的问题(BUG)。随着编程的经验的提高,和对开发的理解不断提高自己的设计能力。不如把两种方法结合起来。在开发中重构,在重构中设计。开发前期,有章法可寻;后期通过不断重构完善设计。在设计和重构之间相辅相成
  • 重构可以带来更简单的设计,同时又不损失灵活性,也降低了设计过程中的难度,减轻了设计压力。

劳而不获

  • 在软件开发的过程中,常常会遇到一些性能问题。(学员信息管理系统、帐号管理)
  • 哪怕你完全了解系统,也请实际度量他的性能问题,不要臆测。臆测会让你学到一些东西,但十有八九是错的

2.7 重构与性能

  • 并不赞同提高设计的纯洁性二忽略性能,把性能依赖于硬件上
  • 三种快速软件的编写方法:
    1. 时间预算法:适用于实时系统
    2. 持续关注法:要求程序员在任何时间上做任何事情,都要设法保持系统的高性能。
    3. 利用90%以上的统计数据:在开发前期不用特别关注性能问题,后期在集中优化(用性能度量工具,注意要把握好测试的幅度,不可过大)
  • 重构可以帮助我写出更好更快的软件,在短期看来的确可能使软件变慢,但在优化阶段的软件性能调整上更容易,最终还会的到更好的效果

2.8 重构起源何处

  • 来源与程序员对代码的理解,起到推动作用的是Ward Cunningham & kent Beck下使用Smalltalk