能够说,任何软件系统从设计部署好的次日起,就都变成了 既有代码(existing code)。一个几年的系统和一个几周的系统中存在的问题,并没有本质上的差别。react
俗话说,创业容易守业难。比新搭建一个系统更常见的工做,正是对既有系统的平常维护,通常包括:架构
如何有效进行这些工做,而不是陷入“修复一个bug,还你几个新bug”的恶性循环,是每一个开发者必然面对的课题。函数
既有代码的一种极端状况,就 是遗留代码(legacy code),通常指那些无人再维护的,或架构很是过期,亦或运行在老旧的操做系统上的代码。性能
相比于在大抵上每几天就打个照面的既有代码中修修改改;当面对一头雾水的遗留代码时,若是没有正确的方法,即使再当心翼翼,也总会陷入一筹莫展的境地。单元测试
每一个开发者可能都见过奇奇怪怪的各类具体问题,但概括起来,主要有这么三种状况:测试
面对以上困境,须要作的就是重构:优化
在不改变代码功能的前提下,改善其设计的行为被称为 重构(refactor)ui
重构的意义:使既有代码更具可维护性,并消除其不肯定性操作系统
重构的关键:在其过程当中不该该有任何功能上的改变设计
在以前提到过的几种平常工做中,无一例外不须要先进行有效的重构,才能保证工做的顺利进行。
一样显而易见的是,将三重困境一一化解,就能够达到理想的重构:
依赖:
封装:
测试:
简而言之,前两项是最后“落在实处”的工做,而测试的重要性并不亚于任何工做,甚至是保证前两项进行下去的关键。 **测试覆盖率(test coverage)**越高,说明所覆盖部分的可靠性越有保证,而没必要时时担忧改动带来的未知影响。
遵循为独立单元(视状况为函数、类或组件等)编写测试的理念,就能够写出小而易理解的一个个测试用例,也反过来使得代码比写注释更容易理解。
对于新开发的功能,能够用测试驱动开发(TDD)的方法,即重复“写一点代码->编写测试->失败->修改代码->测试经过”的过程,最终达到方法的完成。
对于既有代码,能够根据平常需求,对涉及到的部分逐步引入单元测试,持续不断的提升系统的测试覆盖率。
这里举一个足够简单也比较典型的例子:重构stepper组件
在这个由 react 组件构建的既有系统中,在若干界面中都引用了一个常见的 数字选择器(numeric stepper),其变化会触发判断逻辑,提示商品对应的数量是否有足够库存、是否达到了限购数量等
NumberStepper
里糅杂了具体业务逻辑“限购”和“库存”的判断NumberStepper
和各类容器组件中,均分别存在用 0|1|2|3
定义的判断和显示逻辑,且无注释说明困境 | 问题A | 问题B | 问题C |
---|---|---|---|
依赖太重 | √ | √ | |
封装错误 | √ | √ | √ |
缺少测试 | √ | √ |
NumberStepper
中的具体业务逻辑依赖删除NumberStepper
暴露props.checkLimit
,在数量增减时响应,将判断逻辑交给调用者onLimitOver
的函数签名,明确参数的意义NumberStepper
和OverLimit
等组件编写测试,保证改动的全面和正确性至此,以前的结构获得了有效的梳理;再进行相关的功能添加就有章可循、心中有数了。