第二章 重构的原则
2.1 何谓重构
重构分为了动词和名词两种意义。
重构(名词):对软件内部结构的一种调整,目的是不改变软件可观察行为的前提下,提高其可理解性,降低修改成本。
(问题来了,重构真的降低了可理解性吗)
重构(动词):使用一系列重构首发,在不改变软件可观察行为的前提下,调整其结构。
重构和代码清理的区别是:重构的关键在于用大量且微小,保证软件行为的步骤,一步一步达到大规模的修改。每个单独的重构要么很小,要么由若干小步骤组合而成。
(问题来了,我换一个代码架构重写,这个过程算重构吗?从作者的定义来看,这并不算重构,叫做“结构调整”)
如果有人说他们的代码在重构过程中有一两天时间不可用,基本上可以确定,他们做的事不是重构
重构并不等同于性能优化,重构理论上不会带来任何的性能优化,可能会变快,也可能会变慢。性能优化的目标只是使得代码运行的更快,但可能会使得代码变得更难理解。
2.2 两顶帽子
这里作者指的两顶帽子,分别是重构,和添加新功能。
重构:不再添加新功能,且不加任何的测试。
添加功能:添加功能,添加测试。
在编写程序的过程中,你可能会随时的切换这两种状态,但是你要知道,你什么时候戴的是什么帽子。
2.3 为何重构
重构可以改变软件的设计
如果没有重构,程序的内部设计会逐渐腐败变质。因为当人们只为了短期目的而修改代码的时候,他们经常会没有完全理解架构的整体设计,于是代码就逐渐失去了自己的结构。设计就会腐败。
设计欠佳的程序修改起来,成本会很大。
(我个人觉得,目前国内的编码环境,很少有人能够愿意抽出时间来交给大家来进行重构,大家都只会提出一些短期目标,自然而然代码就越来越shi了。代码好不好看,只能取决于写代码的这个人的个人节操了。)
重构使软件更容易理解
计算机根本不在乎你代码的可读性,但是几个月后来维护这个代码的程序员在乎。
重构可以帮助找到bug
因为你重构,你就会去读代码,然后你就会发现bug。
(有时候重构也会引入bug,但我觉得bug这个东西应该由测试来保证,这个不算是重构的有点)
重构提高编程速度
内部质量良好的软件可以让我们很容易就知道怎么去修改,在哪儿修改。
2.4 何时重构
三次法则:第一次做这件事情,那就只管去做;第二次做类似的失去,你还是可以去做;第三次遇到这件事情,你就应该去重构它。
预备性重构
重构的最佳时机就是在添加新功能之前。
帮助理解的重构:使代码更易懂
如果你觉得这个代码看不懂,就应该重构它。
捡垃圾式重构
如果我发现了垃圾代码,我就应该重构它;但是重构的优先级并没有新增功能的优先级高。
有计划的重构和见机行事的重构
重构并不是一件与编程割裂的行为,你不需要专门安排时间来进行重构。重构是在做某些事情的过程中自然而然的发生的。
但是有计划的重构并不是没有意义的,但大部分重构应该是不起眼且见机行事的。
肮脏的代码必须重构,但漂亮的代码也需要重构。
(这个我不太苟同,我不知道漂亮的定义是什么,我觉得一个代码可读性好,且容易修改,那么他就是一个漂亮的代码)
每次要修改的时候,首先令修改很容易,再进行这次容易的修改。
长期重构
大部分的重构几分钟,几个小时就能搞定。但是部分重构需要几个星期,但是不推荐让一支团队专门负责做重构。建议让大家逐步的迁移过去,这样做的好处是重构不会破坏代码,每次进行小的改动后,整个系统仍然照常工作。例如,如果想替换掉一个正在使用的库,可以先引入一层新的抽象,使其兼容新旧两个库的接口。一旦调用方已经完全改为使用这层抽象,替换下面的库就会容易得多。
这个策略叫做 “Branch By Abstraction[mf-bba]”
(强烈赞同!!!!!重写整个架构,然后打算无损替换过去,这种行为是真的傻逼。先立个flag,我这辈子不会干第二次这种事情了。)
code review 的时候重构
如果说,在进行cr的时候,写代码的人能够和审核代码的人在一起,效果会非常好。
这个叫做结对编程。
怎么对经理说
如果经理不懂技术,你就别和经理说。
如果懂技术,直说就行。
(建议默认经理不懂技术比较好,反正经理不care你究竟是怎么写的代码,他们只在乎结果而已。)
何时不应该重构
只有在我需要理解其工作原理的时候,我才需要去重构它,否则对于我而言是没有价值的。
另外一种情况是,重写比重构更简单。
重构的挑战
重构是有挑战的。
延缓新功能的开发
重构的唯一目的是让我们开发更快,用更少的工作量创造更大的价值。
重构需要花费时间,如果添加功能非常小,你可以先把功能加上,再进行重构,这个需要程序员的专业判断。
(你就是hxd?)
从较多的情况而言,重构不足的情况远大于重构过度的情况,所以绝大多数人应该尝试多做重构。代码库的健康与否,到底会对生产率造成多大的影响,很多人可能说不出来,因为他们没有太多在健康的代码库上工作的经历————轻松地把现有代码组合配置,快速构造出复杂的新功能,这种强大的开发方式他们没有体验过。
如果你是leader,你应该向团队表示,你是重视重构的,对于缺乏重构经验的年轻人要有意的知道,才能帮助他们加速经验的积累。
重构并不需要使得代码变得很好快,而是使得代码能够添加功能更快,修复bug更快。重构提高的是经济利益。
代码的所有权
推荐团队所有制,让大家拥有更多的权限。
分支
特性分支存在的时间越短越好,应该频繁的从master同步代码。
测试
你应该使得你的代码是自测试的,这样就会使得能够快速发现错误。
有人会觉得满足自测试的代码,这个对团队的要求会很高。但是从作者的角度而言,团队投入时间和精力在测试上的收益绝对是划算的。
(国内就别想了,上线前能肉眼看一下log是否符合预期就算不错了。。。更别说自测试了)
遗留代码
遗留代码的特征是,无测试,复杂,且代码是别人的。
建议再买一本《修改代码的艺术》书来读。
数据库的修改
假设你要改一个字段的名,正确的做法是新建一列,然后双写,直到测试完成后,再删掉之前的那列。
2.6 重构、架构和YAGNI
在很久之前,有人会说,在开始写代码的时候,你就应该设计好。
但实际上,做好这个的前提是,你在写软件前就已经充分理解了未来可能的需求,但实际上这个假设是不切实际的。
以前的做法是应该增加灵活性机制,给这个函数增加很多个参数,使得函数更加通用。
但有了重构技术,与其猜测未来需要什么技术,更应该只在乎当前的需求构造软件。随着用户需求的增加,我再进行重构。
这种东西叫做YAGNI,you aren't going to need it. 你不会需要他的~
2.7 重构与软件开发过程
极限编程的三个要点:自测试代码,持续继承,重构。
在这三大核心实践的基础上,才谈得上敏捷开发。这样才可以做到一天多次的发布,且每次发布都是高质量可用的。
2.8 重构与性能
重构并不会优化时间,但是会使得代码可以调优。你的做法是先写出调优的软件,然后再调优它以求获取更加足够的速度。
性能的优化请不要靠想像,应该由数据,图表来证明。
作者的做法是,做好监控,在实现功能期间并不在乎性能,在真正在乎性能的时候,再专门的去优化性能。
2.9 重构的起源
这句话全是废话
2.10 自动化重构
JB系列套件很牛逼。