# 第2章:重构原则

loading

# 一、何谓重构

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

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

重构的目的是使软件更容易被理解和修改。你可以在软件内部做很多次修改,但必须对软件可观察的外部行为只造成很小变化,或甚至不造成变化。与之形成对比的是性能优化。和重构一样,性能优化通常不会改变组件的行为(除了执行速度),只会改变其内部结构。但是两者出发点不同:性能优化往往使代码较难理解,但为了得到所需的性能你不得不那么做。

重构不会改变软件可观察的行为——重构之后软件功能一如以前。任何用户,不论最终用户或其他程序员,都不知道有东西发生了改变。

使用重构技术开发软件时,把自己的时间分配给两种截然不同的行为:添加新功能以及重构。添加新功能时,不应该修改既有代码,只管添加新功能。通过测试,你可以衡量自己的工作进度。重构时就不能再添加功能,只管改进程序结构。此时不应该添加任何测试,只在绝对必要时才修改测试。

# 二、为何重构

  • 重构改进软件设计

改进设计的一个重要方向就是消除重复代码。这个动作的重要性在于方便未来的修改。代码量减少并不会是系统运行更快,因为这对程序的运行轨迹几乎没有任何明显影响。

  • 重构使软件更容易理解

  • 重构帮助找到 bug

  • 重构提高编程速度

# 三、何时重构

  • 三次法则

事不过三,三则重构

  • 添加功能时重构

  • 修补错误时重构

  • 复审代码时重构

# 四、怎么对经理说

由于软件工程师对间接层如此醉心,你应该不会惊讶大多数重构都为程序引入了更多间接层,重构往往把大型对象拆成多个小型对象,把大型函数拆成多个小型函数。但是间接层有它的价值。

  • 允许逻辑共享

比如说一个子函数在两个不同的地点被调用,或超类中的某个函数被所有子类的共享。

  • 分开解释意图和实现

你可以选择每个类和函数的名字,这给了你一个解释自己意图的机会。累活函数内部则解释实现这个意图的做法。如果类和函数内部又以更小单元的意图来编写,你所写的代码就可以描述其结构中的大部分重要信息。

  • 隔离变化

很可能在两个不同地点使用同一对象,其中一个地点我想改变对象行为,但如果修改了它,我就要冒同时影响两处的风险。为此我做出一个子类,并在需要修改处引用这个子类。现在,我可以修改这个子类而不必承担无意中影响另一处的风险。

  • 封装条件逻辑

对象有一种奇妙的机制:多态消息,可以灵活而清晰地表达条件逻辑。将条件逻辑转化为消息形式,往往能降低代码的重复、增加清晰度并提高弹性。

还有一种比较少见的重构游戏:找出不值得的间接层,并将它拿掉。

# 五、重构的难题

  • 数据库

在非对象型数据库中,解决这个问题的办法之一就是:在对象模型和数据库模型之间插入一个分割层,这就可以隔离两个模型各自的变化。升级某一模型时无需同时升级另一模型,只需升级上述的分割层即可。这样的分割层会增加系统复杂度,单可以给你带来很大的灵活度。如果你同时拥有多个数据库,或如果数据库模型较为复杂使你难以控制,那么即使不进行呢重构,这分割层也是很重要的。

  • 修改接口

不要过早发布接口,请修改你的嗲吗所有权政策,使重构更顺畅

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

比如说在一个项目中,我们很难(但还是有可能)将不考虑安全性需求时构造起来的系统重构为具备良好安全性系统。这种情况下办法就是:先想象重构的情况。如果看上去很简单,就不必太担心选择是否得当。单如果预先看不到简单的重构办法,就应该在设计上投入更多力气。

  • 何时不该重构

有时候机油代码实在太混乱,重构还不如重新写一个来的简单。重写(而非重构)的一个清楚讯号就是:现有代码根本不能正常运作。

一个这种的办法就是:将“大块头软件”重构为封装良好的小型组件,然后就可以逐一对组件做出“重构或重建”的决定。

另外,如果项目已近最后期限,应该避免重构。

# 六、重构与设计

要建造一个灵活的解决方案,所需的成本难以估算。灵活的解决方案比简单的解决方复杂许多,所以最终得到的软件通常也会更难维护——虽然它在我预先摄像的方向上的确是更加灵活。就算幸运地走在预先摄像的方向上,你也必须理解如何修改设计。如果变化只出现在一两个地方,那不算大问题。然而变化其实可能出现在系统各处。如果在所有可能的而变化出现地点都建立起灵活性,整个系统的复杂度和维护难度都会大大提高。

有了重构,你就可以通过一条不同的途径来应付变化带来的风险。你仍旧需要思考潜在的变化,仍旧需要考虑灵活的解决方案。但是不必在逐一实现这些解决方案,而是应该问问自己:“把一个简单的解决方案重构成这个灵活的方案有多大难度?”如果答案是“相当容易”,那么你就只需实现目前简单的方案就行了。

重构可以带来更简单的设计,同时又不损失灵活性,这也降低了设计过程的难度,减轻了设计压力。

# 七、重构与性能

首先写出可调的软件,然后调整它以求获得足够速度。

三种编写快速软件的方法:

  1. 时间预算法

性能要求极高的实时系统,分解好设计时就要做好预算,给每个组件预先分配一定资源——包括时间和执行轨迹。每个组件绝对不能超出自己的预算,就算拥有组件之间的调度预配时间的机制也不行。这种方法高度重视性能,对于心律调节器一类的系统是必须的,因为在这样的系统中迟来的数据就是错误的数据。

  1. 持续关注法

要求任何程序员在任何时间做任何事时,都要设法保持系统的高性能。

  1. 利用程序分析的90%统计数据

不对性能投以特别的关注,直至进入性能优化阶段——通常是开发后期。进入这个阶段后,再按照某个特定程序来调整程序性能。

# 八、重构起源何处

优秀程序员肯定会花一些时间来清理自己的代码。这么做是因为,他们知道简介的代码比杂乱无章的代码更容易修改,而且他们知道自己几乎无法一开始就写出简洁的代码。


上次更新: 2020-08-21 09:02:51(10 小时前)