跳转至

1.何为重构

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

重构(动词):使用一系列重构手法,再不改变软件可观察行为的前提下,调整其结构。

如果有人说他们的代码在重构的过程中有一两天时间不可用,基本可以确定,他们在做的不是重构。

2.两顶帽子

使用重构技术开发软件时,我把自己的时间分配给两种截然不同的行为:新功能重构

添加新功能时,不应该修改既有代码,只管添加新功能。

重构时就不能添加新功能,只管调整代码结构。

3.为何重构

银弹:是指利器,解决问题的方法或技术手段。

  1. 重构改进软件的设计

    如果没有重构,程序内部设计(或者叫架构)会逐渐腐败变质。

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

    重构可以帮助我们更容易读懂代码。

  3. 重构帮助找Bug

    重构代码的过程,可以深入理解代码的行为,同时也可以验证一些假设,可以更好的发现Bug。

  4. 重构提高编程速度

    添加新功能时,内部良好的代码很容易找到在哪里修改、如何修改。引入的bug可能性就会变小。调试也容易的多。

4.何时重构

事不过三、三则重构

第一次做某件事时,只管去做;

第二次做类似的事会产生反感,但无论如何还是可以去做;

第三次在做类似的事,你就应该去重构;

  1. 预备性重构:让添加新功能更加容易

    在动手添加新功能之前,先看看代码,此时会经常发现对代码进行微调,工作会容易的多。

  2. 帮助理解的重构:使代码更容易理解

    看代码时,在脑海里会形成一些理解,但是记不住那么多细节,通过重构,把本身的理解转移至代码本身。

    例如:变量名修改、将一个长函数拆分。

    缺少了这些细微的整理,就无法看到隐藏在一片混乱后的机遇。

  3. 捡垃圾式重构

    我们已经理解代码在做什么,但发现他做的不好(例如:逻辑不必要的迂回复杂、两个函数几乎完全一致)。

    这里有一个取舍:不想在眼下的任务跑题,但又不想把垃圾留在原地,导致将来的麻烦。

    如果发现很容易重构,会马上重构它;过于复杂,则用便签记录,在完成当下任务后重构。

  4. 有计划的重构和**见机行事**的重构

    肮脏的代码必须重构,但漂亮的代码也需要很多重构。

    每当需要新能力时,软件就应该作出相应的改变。越是在已有的代码里,这样的改变就越显重要。

  5. 长期重构

    让团队达成共识,在未来几周时间里逐步解决这个问题,这经常是一个有效的策略。

    如果想替换掉一个正在使用的库,可以先引入一层新的抽象,使其兼容旧的两个库的接口。一旦调用方已经完全改为使用这层抽象,替换下面的库就会很容易。(这个策略叫做 Branch By Abstracton[mf-bba])

  6. 复审代码时重构

    重构可以帮助我们复审别人的代码,开始重构前可以先阅读代码,得到一定程度理解,并提出一些建议。

    重构还可以帮助代码复审工作得到具体的结果。

  7. 怎么对经理说

    比较有争议的话,不要告诉经理!

  8. 何时不应该重构

    可以容忍的丑陋。

    重写比重构更加简单。

5.重构的挑战

有必要了解到重构会遇到的挑战,这样才能做出幼小的应对。

  1. 延缓新功能开发

    重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值。

    1. 如果做一点重构能让新功能实现更容易,那一定要做。
    2. 如果一个问题已经见过了,那一定会重构(反之,一块代码很少碰触,不会经常带来麻烦,那就比较倾向于不重构)。
    3. 如果还不太清楚如何重构,应该先延迟重构。
    4. 有时候没想清重构的方向,先做实验后,再决定是否重构。

    5. 重构的意义不在于把代码打磨的闪闪发光,而是因为他可以让我们更快的--添加新功能、快速修复bug

  2. 代码所有权

    代码所有权的边界会妨碍重构,因为一旦我自作主张的修改,就一定会破坏使用者的程序,

    • 开源型团队:鼓励A团队 修改 B团队的代码,然后送给B团队审核。
  3. 分支

    分支的合并本来就是一个复杂性的问题,随着特性分支存在的时间加长,合并的难度就会呈指数上涨。

    • 持续集成(Continuous Integration,CI):在使用CI时,每个团队成员每天至少向主线集成一次。这个实践避免了人和峰值彼此差异太大,从而极大的降低了合并的难度。不过CI也有其代价:你必须使用相关的实践保证主线随时处于健康状态,必须学会将大功能拆分成小块,还必须使用特性开关(feature , 也叫特性旗帜 ,feature flag)将尚未完成且不能拆分的功能隐藏掉。
  4. 测试

    自测的代码不仅仅是重构成之为可能,而且添加新功能更加安全,因为我可以很快的发现并干掉新bug。

    自测代码持续集成 紧密相关 ---- 我们依赖持续集成来及时捕获分支集成时的语义冲突。自测试代码是极限编程的另一个重要组成部分,也是持续交付的关键环节。

  5. 遗留代码

    不建议尝试一鼓作气把复杂而混乱的一流代码重构成漂亮的代码。

    最好是:每次碰触代码是,尝试把它变好一点点。如果是一个大系统,越是频繁使用的代码,改善其可理解性的努力就能得到越丰厚的回报。

  6. 数据库

    渐进式数据库设计**和**数据库重构

    这项技术的精要在于:借助数据迁移脚本,将数据库结构的修改与代码相结合,使大规模的、涉及数据库的修改比较容易的开展。

    数据库重构最好要分散到多次生产发布来完成,这样每次迁移之后系统任然可以运行起来。即便出现问题也很容易修改回滚。

6.重构、架构和YAGNI

  • **重构对架构的影响**可以得到一个良好的代码库,使其能优雅的应对不断变化的需求。
  • 简单设计、增量式设计 或 YAGNI (you aren‘t going to need it , 你不会需要它)。
  • 如今我们倾向于等一等,待到问题理解更加充分,再来着手解决。(演进式框架)是一门不断发展的学科,架构师们在不断探索有用的模式和实践,充分发挥迭代式架构的决策能力。

7.重构与软件开发过程

极限编程时最早的敏捷开发软件方法之一。

重构的第一块基石是 自测试代码

  1. 自测试代码
  2. 持续集成
  3. 重构

8.重构与性能

首先,他让我们有充裕的时间进行性能调整,因为有良好的代码在手,能够快速的添加功能,也试试有更多的时间用在性能问题上。

其次,在进行性能分析时,便有较细的颗粒度。度量工具会把我们带入一个更小的范围内,而性能调整就更加容易一些。

由于代码的清晰,因此就能更好理解自己的选择,更清楚那种调整其着关键作用