1、重构:对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
2、使用重构技术开发软件时,把自己的时间分配给两种截然不同的行为:添加新功能,以及重构。添加新功能时,不应该修改既有代码,直观添加新功能。重构时不能再添加新功能,只管改进程序结构。需要清楚自己正在做哪一块工作。
3、为何重构:
(1)重构改进软件设计。
完成同样一件事,设计不良的程序往往需要更多代码,这常常是因为代码在不同的地方使用完全相同的语句做同样的事情。因此改进设计的一种重要方向就是消除重复代码。代码越多,正确的修改就越困难,因为有更多的代码需要理解。如果消除重复代码,你就可以确定所有事物和行为在代码中只表述一次,这正是优秀设计的根本。不要只为短期的目的。
(2)重构使软件更容易理解。
程序猿需要考虑今后将会有另外一个读者,会来修改代码。所以代码需要尽量表现出准确的功能。一开始所做的重构可能只停留在细枝末节上,随着代码渐趋简洁,可以发现一些以前看不到的设计层面的东西。如果不对代码做这些修改,我们也许永远看不见它们,因为我们的才智不足以将一切想象出来。重构可以将我们带到更高的理解层次。
(3)重构可以帮助找到bug。
如果对代码进行重构,我们可以深入理解代码的行为,并恰到好处地把新的理解反馈回去。搞清楚程序结构的同时,也能帮助找到bug。重构能够帮助我们写出更健壮的代码。
(4)重构可以提高编程速度。
改善设计、提升可读性、减少错误,这些都是提高质量,同时也会提高开发进度。
良好的设计是快速开发的根本。如果没有良好的设计,或许某一段时间内,你的进展迅速,但恶劣的设计很快让你的速度慢下来。你会把时间花在调试上面,无法添加新功能。(深有体会啊。。。)
4、何时重构:
几乎任何情况下我都反对专门拨出时间进行重构。在我看来,重构本来就不是意见应该特别拨出时间做的事情,重构应该随时随地进行。第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是可以去做;第三次再做类似的事,你就应该重构。
(1)添加功能时重构:
重构的直接原因往往是为了帮助我理解需要修改的代码——这些代码可能是别人写的,也可能是我自己写的。无论何时,只要我想理解代码所做的事就会尝试着冲能够。在尝试的过程中把代码的结构理清,我就可以从中理解更多的东西。另外,可能是因为代码的设计无法帮助我轻松添加我所需要的特性。重构之后也能使未来添加新特性的时候,更加快速、流畅。
(2)修补错误时重构:
调试过程中运用重构,多半是为了让代码更具可读性。当我看着代码并努力理解它的时候,我用重构帮助加深自己的理解。我发现以这种程序来处理代码,常常能够帮助我找出bug。
(3)复审代码的重构:
很多公司都会做常规的代码复审(表示木有。。。),因为这种活动可以改善开发状况。这种活动有助于在开发团队中传播知识,也有助于让较有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人理解大型软件系统中的更多部分。代码复审对于编写清晰的代码非常重要,我的代码对于我来说清晰,但对于其他人则不然。代码复审也让更多人有机会提出有用的建议。复审团队必须保持精炼,最好是一个复审者搭配一个原作者,共同处理这些代码。复审者提出修改意见,然后两人共同判断这些修改是否能够通过重构轻松实现。如果果真如此,就一起动手修改。
5、为什么重构有用?
程序有两面价值:“今天可以为你做什么”和“明天可以为你做什么”。大多数时候,我们都只关注自己今天想要程序做什么。不论是修复错误或者添加特性,我们都是为了让程序能力更强,让它在今天更有价值。
但是系统当下的行为,只是整个故事的一部分,如果没有认清这一点,你无法长期从事编程工作。如果你为求完成今天的任务而不择手段,导致不可能在明天完成明天的任务,那么最终还是会失败。但是,你知道自己今天需要什么,却不一定知道自己明天需要什么。也许你可以猜到明天的需求,也许吧,但肯定还有些事情出乎你的意料。(Head First Design Patterns:Change is constant)
对于今天的工作,我了解得很充分;对于明天的工作,我了解得不够充分。但如果我纯粹只是为今天工作,明天我将完全无法工作。
重构是一条摆脱困境的道路。如果你发现昨天的决定已经不适合今天的情况,放心改变这个决定就是,然后你就可以完成今天的工作了。明天回头看今天的理解也许觉得很幼稚,那时你还可以改变你的理解。
是什么让程序如此难以相与?眼下我能想起下述四个原因,它们是:
(1)难以阅读的程序,难以修改;
(2)辑重复的程序,难以修改;
(3)添加新行为时需要修改已有代码的程序,难以修改;
(4)带复杂条件逻辑的程序,难以修改
因此,我们希望程序:(1)容易阅读;(2)所有逻辑都只在唯一地点指定;(3)新的改动不会危及现有行为;(4)尽可能简单表达条件逻辑
6、间接层和重构
计算机科学是这样一门科学:它相信所有问题都可以通过增加一个间接层来解决。
由于软件工程师对间接层如此醉心,你应该不会惊讶大多数重构都为程序引入更多间接层。重构往往把大型对象拆成多个小型对象,把大型函数拆成多个小型函数。
但是,间接层是一柄双刃剑。每次把一个东西分成两份,你就需要多管理一个东西。如果某个对象委托另一个对象,后者又委托另一个对象,程序会越加难以阅读。
基于这个观点,你会希望尽量减少间接层。但是间接层有它的价值:
(1)允许逻辑共享。比如说一个子函数(对应另外一个应该是主函数了)在两个不同的地点被调用,或超类中的某个函数被所有子类共享(这个应该是继承和多态了)。
(2)分开解释意图和实现。你可以选择每个类和函数的名字,这给了你一个解释自己意图的机会。类或函数内部则解释实现这个意图的做法。如果类和函数内部又以更小单元的意图来编写,你所写的代码就可以描述其结构中的大部分重要信息。(简单可以理解为,类名应该能表现出这是一个什么类,函数名应该能表示其功能,随着重构,代码块变小,代码的结构就慢慢清晰)
(3)隔离变化。很可能我在两个不同地点使用同一个对象,其中一个地点我想改变对象行为,但如果修改了它,我就要冒同时影响两处的风险。为此我做出了一个子类,并在需要修改处引用这个子类。现在,我可以修改这个子类而不必承担无意中影响另一处的风险。
(4)封装条件逻辑。对象有一种奇妙的机制:多态消息,可以灵活而清晰地表达条件逻辑。将条件逻辑转化为消息形式(这个。。。不大理解,你们理解吗?感觉有点抽象),往往能降低代码的重复、增加清晰度并提高弹性。
这一章讲的基本上是重构原则上的知识。摘出了大半部分吧,没有摘出来的包括
(1)需要重构的时候,怎么跟经理说
(2)修改接口的一些想法
(3)何时不应该重构
(4)重构与性能
。。。等等,感兴趣的可以去看看