书摘:重构--改善既有代码的设计

2015-04-16 23:31:59
正是这样的小步与缓步前进,不过激,不躁进,再加上完整的测试配套〔是的,测试之于重构极其重要),才是「不带来破坏,不引入臭虫」的最佳保障。

2015-04-16 23:33:33
我十分同意Martin说『你会发现所谓设计不再是一切动作的前提,而是在整个开发过程中逐渐浮现出来。』

2015-04-16 23:34:05
重型开发和轻型开发各有所长,各有应用,世间并无万应灵药,任何东西都不能极端。过犹不及,皆不可取!

2015-04-17 21:39:29
你挖掘自己的代码,很快发现了一些值得修改的地方,于是你挖得更深。挖得愈深,找到的重构机会就越多…于是你的修改也愈多。最后你给自己挖了个大坑,却爬不出去了。

2015-04-18 06:31:27
差劲的系统是很难修改的,因为很难找到修改点。如果很难找到修改点,程序员就很有可能犯错,从而引入「臭虫」(bugs)。

2015-04-18 06:33:15
你的态度也许倾向于「尽量少修改程序」:不管怎么说,它还运行得很好。你心里头牢牢记着那句古老的工程学格言:「如果它没坏,就别动它」。这个程序也许还没坏掉,但它带来了伤害。它让你的生活比较难过,因为你发现很难完成客户所需的修改。

2015-04-18 06:33:38
如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地那么做,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性。

2015-04-18 07:29:17
每当我要进行重构的时候,第一个步骤永远相同:我得为即将修改的代码建立一组可靠的测试环境。这些测试是必要的,因为尽管遵循重构准则可以使我避免绝大多数的臭虫引入机会,但我毕竞是人,毕竟有可能犯错。所以我需要可靠的测试。

2015-04-18 07:30:04
必须让测试有能力自我检验,否则就得耗费大把时间来回比对,这会降低你的开发速度。

2015-04-18 07:30:30
好的测试是重构的根本。花时间建立一个优良的测试机制是完全值得的,因为当你修改程序时,好测试会给你必要的安全保障。

2015-04-18 07:33:34
被修改的变量都可以被我当成参数传入新的函数,至于会被修改的变量就需格外小心。如果只有一个变量会被修改,我可以把它当作返回值。

2015-04-18 07:44:31
重构步骤的本质:由于每次修改的幅度都很小,所以任何错误都很容易发现。

2015-04-18 07:46:05
更改变量名称是值得的行为吗?绝对值得。好的代码应该清楚表达出自己的功能,变量名称是代码清晰的关键。如果为了提高代码的清晰度,需要修改某些东西的名字,大胆去做吧。只要有良好的查找丨替换工具,更改名称并不困难。

2015-04-18 07:46:32
代码应该表现自己的目的,这一点非常重要。

2015-04-18 07:53:46
运用Move Method。首先把代码拷贝到Rental class 内, 调整代码使之适应新家,然后重新编译。

2015-04-18 07:53:55
下一个步骤是找出程序中对于旧函数的所有引用(reference)点,并修改它们,让它们改用新函数。

2015-04-18 07:54:30
有时候我会保留旧函数,让它调用新函数。如果旧函数是一个public 函数,而我又不想修改其他class 的接口,这便是一种有用的手法。

2015-04-18 20:32:47
为了保证被执行的的确是subclass 代码,我喜欢故意丢一个错误进去,然后让它运行,让测试失败。

2015-04-18 22:57:49
和重构一样,性能优化通常不会改变组件的行为(除了执行速度),只会改变其内部结构。但是两者出发点不同:性能优化往往使代码较难理解,但为了得到所需的性能你不得不那么做。

2015-04-18 22:58:46
使用重构技术开发软件时,你把自己的时间分配给两种截然不同的行为:「添加新功能」和「重构」。添加新功能时,你不应该修改既有代码,只管添加新功能。通过测试(并让测试正常运行〉,你可以衡量自己的工作进度。重构时你就不能再添加功能,只管改进程序结构。此时你不应该添加任何测试(除非发现先前遗漏的任何东西),只在绝对必要(用以处理接口变化〕时才修改测试。

2015-04-18 22:59:14
软件开发过程中,你可能会发现自己经常变换帽子。首先你会尝试添加新功能,然后你会意识到:如果把程序结构改一下,功能的添加会容易得多。于是你换一顶帽 子,做一会儿重构工作。程序结构调整好后,你又换上原先的帽子,继续添加新功能。新功能正常工作后,你又发现自己的编码造成程序难以理解,于是你又换上重构帽子……。整个过程或许只花十分钟,但无论何时你都应该清楚自己戴的是哪一顶帽子。

2015-04-19 08:44:16
一开始我所做的重构都像这样停留在细枝末节上。随着代码渐趋简洁,我发现自己可以看到一些以前看不到的设计层面的东西。如果不对代码做这些修改,也许我永远看不见它们,因为我的聪明才智不足以在脑子里把这一切都想像出来。Ralph
Johnson把这种「早期重构」描述为「擦掉窗户上的污垢,使你看得更远」。研究代码时我发现,重构把我带到更高的理解层次上。如果没有重构,我达不到这种层次。

2015-04-21 07:06:09
我不是个伟大的程序员;我只是个有着一些优秀习惯的好程序员

2015-04-21 07:06:34
良好设计是快速软件开发的根本。

2015-04-21 07:06:49
如果没有良好设计,或许某一段时间内你的进展迅速,但恶劣的设计很快就让你的速度慢下来。

2015-04-21 07:07:36
重构本来就不是一件「特别拨出时间做」的事情,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好。

2015-04-21 07:18:32
第一次做某件事时只管去做;第二次做类似的事会产生反感,但无论如何还是做了;第三次再做类似的事,你就应该重构。

2015-04-21 07:19:19
事不过三,三则重构(Three strikes and you refactor)

2015-04-21 10:46:02
常态性的代码复审工作(code reviews),因为这种活动可以改善开发状况。这种活动有助于在幵发团队中传播知识,也有助于让较有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人理解大型软件系统中的更多部分。代码复审工作对于编写清晰代码也很重要。我的代码也许对我自己来说很清晰,对他人则不然。这是无法避免的,因为要让幵发者设身处地为那些不熟悉自己所做所为的人设想,实在太困难了。代码复审也让更多人有机会提出有用的建议,毕竟我在一个星期之内能够想出的好点子很有限。如果能得到别人的帮助,我的生活会舒服得多,所以我总是期待更多复审。

2015-04-21 10:46:40
复审团队必须保持精练。就我的经验,最好是一个复审者搭配一个原作者,共同处理这些代码。复审者提出修改建议,然后两人共同判断这些修改是否能够通过重构轻松实现。果真能够如此,就一起着手修改。

2015-04-24 12:15:00
系统当下行为,只是整个故事的一部分,如果没有认清这一点,你无法长期从事编程工作。

2015-04-24 12:15:40
如果你发现昨天的决定已经不适合今天的情况,放心改变这个决定就是,然后你就可以完成今天的工作了。明天,喔,明天回头看今天的理解也许决定幼稚,那是你还可以改变你的理解。

2015-04-25 07:09:24
把重构当作「将复审意见引入代码内」的方法来使用

2015-04-25 07:20:27
如果项目已近最后期限,你也应该避免重构。

2015-04-25 07:20:52
未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转。但是借债就得付利息,过于复杂的代码所造成的「维护和扩展的额外开销」就是利息。你可以承受一定程度的利息,但如果利息太高你就会被压垮。把债务管理好是很重要的,你应该随时通过重构来偿还一部分债务。

2015-04-25 17:19:39
软件和真实器械有着很大的差异。软件的可塑性更强,而且完全是思想产品。正如Alistair Cockburn所说:『有了设计,我可以思考更快,但是其中充满小漏洞。』

2015-04-25 17:27:23
当下只管建造可运行的最简化系统,至于灵活而复杂的设计,唔,多数时候你都不会需要它。

2015-04-25 17:29:28
哪怕你完全了解系统,也请实际量测它的性能,不要臆测。臆测会让你学到一些东西,但十有八九你是错的。

2015-04-26 20:32:35
如果你能给函数起个好名字,读者就可以通过名字了解函数的作用,根本不必去看其中写了些什么。

2015-04-26 20:34:28
就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。

2015-04-27 08:10:57
编写优良的测试程序,可以极大提高我的编程速度,即使不进行重构也一样如此。这让我很吃惊,也违反许多程序员的直觉

2015-04-27 08:11:23
认真观察程序员把最多时间耗在哪里,你就会发现,编写代码其实只占非常小的一部分。有些时间用来决定下一步干什么,另一些时间花在设计上面,最多的时间则是用来调试(debug)。

2015-04-27 08:13:03
确保所有测试都完全自动化,让它们检查自己的测试结果。

2015-04-27 19:39:56
撰写测试代码的最有用时机是在开始编程之前。当你需要添加特性的时候,先写相应测试代码。听起来离经叛道,其实不然。编写测试代码其实就是在问自己:添加这个功能需要做些什么。编写测试代码还能使你把注意力集中于接口而非实现上头(这永远是件好事)。

2015-04-27 19:40:32
本书谈的是重构,而重构需要测试。如果你想重构,你就必须编写测 试代码。

2015-04-27 19:45:20
单元测试是高度本地化(localized)的东西,每个test class只对单一package运作。它能够测试其他packages的接口,除此之外它将假设其他一切正常。

2015-04-28 06:56:30
观察class该做的所有事情,然后针对任何一项功能的任何一种可能失败情况,进行测试。这不同于某些程序 员提倡的「测试所有public函数」。记住,测试应该是一种风险驱动(risk driven)行为,测试的目的是希望找出现在或未来可能出现的错误。

2015-04-28 06:57:03
哪怕只做一点点测试,你也能从中受益。测试的要诀是:测试你最担心出错的部分。这样你就能从测试工作中得到最大利益。

2015-04-28 06:57:19
编写未臻完善的测试并实际运行,好过对完美测试的无尽等待。

2015-04-28 08:40:16
花合理时间抓出大多数臭虫」要好过「穷尽一生抓出所有臭虫」。

2015-04-28 08:47:27
这些笔记是为了让我在一段时间不做某项重构之后还能记得怎么做。

2015-04-28 08:51:17
范例是为了帮助解释重构的基本要素,最大限度地避免其他枝节,所以我希望你能原谅其中的简化工作(它们当然不是优秀商用对象设计的适当例子)。不过我敢肯定你一定能在你手上那些更复杂的情况中使用它们。某些十分简单的重构干脆没有范例,因为我觉得为它们加上一个范例不会有多大意义。

2015-04-28 08:52:42
修改后的代码可能被埋没在未修改的代码中,难以一眼看出,所以我使用粗体(boldface
code)突显修改过的代码。但我并没有对所有修改过的代码都使用粗体字,因为一旦修改过的代码太多,全都粗体反而不能突显重点。
注: 实用主义

2015-04-28 14:45:46
不要盲目地「查找-替换」。你应该检查每一个引用点,确定它的确指向你想要替换的东西。

2015-04-28 14:51:18
重构的基本技巧——小步前进、频繁测试——已经得到多年的实践检验

2015-05-01 15:17:46
重构

2015-05-01 15:17:52
大一部分是对函数进行整理,使之更恰当地包装代码。

2015-05-01 17:10:01
只有当你能给小型函数很好地命名时,它们才能真正起作用,所以你需要在函数名称下点功夫。

2015-05-01 17:10:21
创造一个新函数,根据这个函数的意图来给它命名(以它「做什么」来命名, 而不是以它「怎样做」命名)。

2015-05-01 17:10:35
如果你想不出一个更有意义的名称,就别动。

多看笔记 来自多看 for iOS