一、重构的定义
软件设计大师Martin Fowler对重构的定义是:重构是一种对软件内部结构的改善,目的是不改变软件的可见行为的情况下,使其更易理解,修改成本更低。
这里有一点需要强调一下,重构不改变外部的可见行为。所以,我们可以简单的理解为:在保持功能不变的前提下,利用设计思想、原则、模式、编程规范等理论来优化代码,修改设计上的不足,提高代码质量。
二、代码重构的“5W1H”
5W+1H:是对选定的项目、工序或操作,都要从原因(何因Why)、对象(何事What)、地点(何地Where)、时间(何时When)、人员(何人Who)、方法(何法How)等六个方面提出问题进行思考。
对于代码重构来说,地点和人员属于与我们要讲的主题关系不大的两点,所以我们重点来看下3W1H:
重构的目的(why)、重构的对象(what)、重构的时机(when)以及重构的方法(how)
1. 重构的目的:为什么要重构(why)
- 重构是适合保证代码质量的手段,如果没有人为的维护,代码总是朝着混乱的方向演进
- 优秀的代码或者架构并不是一开始就设计好的,随着迭代重构是不可避免的
- 避免过度设计,做到有的放矢
- 对一个工程师本身的成长具有重要的意义
2. 重构的对象:到底重构什么(what)
根据重构的规模,可以分为大型重构和小型重构。
所谓大型重构,是对顶层代码设计的重构,包括:系统、模块、代码结构、类与类之间的关系等,重构的手段有分层、模块化、解耦、抽象可复用组建等等。重构的工具就是设计思想、设计原则以及设计模式。
而小型重构主要是针对类、函数、变量等代码级别的重构,比如规范命名、规范注释、消除超大类或者函数、提取重复代码等等。主要工具是我们所约定的一些代码规范等。
3. 重构的时机:什么时候重构(when)
一定要建立持续重构意识,把重构作为开发必不可少的部分,融入到日常开发中,而不是等到代码出现很大问题的时候,再大刀阔斧地重构。
平时没有事情的事情就可以看看哪些写的不够好的、可以优化的代码,主动去重构一下,这种持续重构的意识比重构本身更加重要。
4. 重构的方法:又该如何重构(how)
对于大型重构来说,我们需要做好完善的重构计划,需要评估影响范围以及如何兼容老的代码逻辑,要分阶段进行,每一个阶段的重构都不要耗时太长,这样才能不与新的功能开发冲突。而小规模重构,影响范围较小,随时都可以去做,此外还可以通过静态代码检查来自动发现代码中的问题,针对性的进行重构优化。
三、代码重构的技术手段:单元测试
1. 什么是单元测试?
单元测试是开发自己编写的用于测试自己代码正确性的代码。相比于集成测试,单元测试更注重类和函数的逻辑是否按照预期执行了,是属于代码层级的测试。
2. 为什么要写单元测试?
- 发现你代码中的bug
- 发现代码设计上的问题,如果单元测试写起来很吃力,那么极有可能代码设计的是不够合理的
- 对集成测试的有力补充,尤其是一些边界条件的测试
- 写单元测试的过程本身就是代码重构的过程
- 阅读单元测试能够帮你快速熟悉代码,实际上单元测试就是用户用例,反映了代码的功能和如何使用
- 单元测试是TDD可落地执行的改进方案(测试驱动开发)
- ....
3. 如何编写单元测试?
这里总结出了一些经验:
(1)写单元测试真的很耗时吗?
确实,过程很繁琐,但是基本都是cv。
(2)对单元测试的代码质量有什么要求吗?
不会产线运行,也不会相互依赖,可以放低要求。
(3)单元测试只要覆盖率高就够了吗?
60到70即可,不必过度追求单元测试覆盖率。
(4)写单元测试需要了解代码的实现逻辑吗?
要着重关心被测函数实现的功能而非内部逻辑。
(5)如何选择单元测试框架?
不需要太复杂的技术,大部分单元测试框架都能满足。
4. 如何在团队中推行单元测试?
首先,100%落实执行单元测试是一件“知易行难”的事。
很多历史代码因为没有单元测试而难以推行,需要每个开发都要有主人翁意识。
此外,程序员应该是智力密集型行业,但是目前很多都是劳动密集型,既没有单元测试,也没有Code Review。写好代码直接提交,然后交给黑盒测试,然后再改bug,如此反复。但实际上,在完善单元测试的情况下,可以很大的减少黑盒测试的投入。
关于单元测试的更多详细信息,可以参考之前的博客中关于单元测试我在团队中的一些实践。