一、初识重构
1.重构是什么?
代码重构是在不修改软件功能的情况下,对软件内部进行调整优化。
2.为什么要进行重构?
- 项目中的代码有明显的难以理解、难以修改的问题
- 在复杂度、重复率方面有严重的问题
- 重构可以把一些效率低的代码,重新调整成效率更高的代码
- 可以将重复提交的代码,为独立的函数
- 统一和规范变量名
3.重构的目标
- 通过更优秀更合理的架构来满足系统高性能、高并发、高可用的需求
- 通过重构来提高代码质量
- 引入新的技术和框架来升级整个系统
- 通过重构来优化业务流程,实现原来实现不了的需求
4.重构的范围
平台级别重构。针对整体平台的重构,如阿里早期是LAMP架构,后来整体迁移到了Java平台。
系统级别重构。针对业务系统的重构,如通过引入微服务架构或者SOA架构,分解单体应用。
架构级别重构。如通过架构的调整和重新设计,改善原有架构的不合理之处。如通过分层使业务解耦,引入缓存设计提升系统高并发等。
业务级别重构。常见为某些业务需求因为系统设计的不合理性导致无法满足或有缺陷满足,需要通过业务系统的重构调整或数据库的重构来解决。
模块/代码级别重构。这是最常见的重构。通常指使用设计模式、封装继承、优化拆解代码,使得代码的结构更良好,运行效率更高。
5.常见的项目重构的方法:
(1)梳理并且分解继承体系
继承是面向对象设计语言中一个很重要的特性,它可以减少子类的代码量。同时继承也会被误用。今天为了一个功能添加了一个小类,也许明天还会为了另外一个功能添加另外一个类。时间一长你就会发现,这个类简直就是惨不忍睹。代码会出现大量的重复,而且修改也会变得很困难。
要修改这个类就要把这个类中相关的变量或者功能梳理清楚,分别给它们建立相应的父类,然后再继承下去。它们分别属于不同的功能体系,必须要有相应的继承体系。
(2)将过程化的代码转化为对象设计
将数据记录编成对象,将大块的行为分成效块并且将行为移入相关的对象之中。常见的场景,类中有很长很长的函数和很少的数据。我们要做的是将这个很长的函数提炼出来放到一个单独的类中来处理。
(3)将程序分层,将数据、界面、逻辑分开
这个就要提到经典的MVC模型,这个模型的价值就在于:它将用户界面和逻辑处理分开了。即界面只包含展示所用的东西;逻辑层只包含逻辑代码而不包含界面的内容。
(4)提炼继承体系
一个类做了很多的事情,其中有些事情是以大量的表达式来完成的,我们应该考虑为这个情况建立起相应的继承体系,使每一个子类包含一种特殊情况。刚开始时,我们设计的时候,是一个类实现一种功能或者一个概念,但是随着时间的推移、方案的改进,可能这个类添加了另外一个概念,变成了两个概念,包含两种功能,随后变成三个四个五个等。最后这个类变得就会完成陌生了,失去了原来我们设计这个类的初衷了。
备注:重构需要对旧系统业务进行梳理,并分批重构。
所以重构是一个解耦的过程?
二、重构性项目如何测试
1.质量标准
TDD或单元测试引入
2.效率标准
(1)方法一:对比测试
测试用例,简单说,就是给定一个场景(输入),验证在这个场景下软件的行为(输出)。所以定义测试用例就需要定义测试时的输入输出和验证规则。
但在代码重构中,因为软件的功能并未增加,一次也就没有增加新的测试用例。并且,重构前的老系统本身就提供了大量的测试用例。为什么这么说,因为重构之后的新系统的各项业务行为只要和老系统保持一致,那就可以说是正确的。
所以,对于代码重构工作,测试用例的编写就被大大简化:只需定义输入,无需定义输出,将老系统的输出作为输出即可。同时,验证规则也简化了很多,各项数据通常只需和老系统保持一致,少数的不能完全一致的数据,只需验证其满足一定规则即可。
具体方法:
定义测试用例输入,分别调用新(重构后的系统)、老系统。用老系统的结果校验新系统,从而降低测试的工作量,提高测试效率。
目前可依赖脚本自动化对比数据,但是脚本编写成本与提升的效率是个需要商榷的问题。
(2)方法二:导流测试
方法一可以帮助简化对测试结果的验证,但是测试输入还是要想办法丰富起来,否则漏掉一个场景就有可能放过一个bug。
对于老功能,需求早已丢失,当年设计开发它的人也已不在,那如何为这样缺失需求的功能设计测试用例。有一个方法是直接使用线上的请求测试。
具体方法:
通过使用Nginx的ngx_http_miror_module模块(或其他技术,如tcpcopy),复制一份线上的请求,然后将这份复制的真实请求导向部署了重构版本应用的服务器。然后再通过方法一介绍的对比测试方式,比较线上应用和重构后应用的输出结果(返回值、数据库记录等等),从而验证代码重构的正确与否。
但是这种测试方法有一定的局限性。简单来说,这种方式适合测试读接口,不太适合或者说是难以测试写接口。因为测试请求来自线上,如果被测服务器同样部署在线上环境,那写接口就会对用户数据造成应用。如果被测服务器部署在测试环境,需要在测试环境完整同步一份线上数据,这需要相当的基础测试设施的支持。