对于大多数开发者来说,在遗留代码基础上开发是日常工作的一部分,毕竟从头开始创建全新系统的机会不是很多。架构师、《漫谈设计模式》作者刘济华结合自身的实际经历分享了如何在遗留代码基础上开发的经验。
刘济华首先指出,大多数系统是构建在之前的遗留系统之上的,在开始,很难把遗留系统直接丢弃,特别是一些业务逻辑非常复杂的金融电信系统。 这些代码往往有如下特点:
但是这些代码能够完成当时的功能,直接抛弃这些代码,重新开发将会耗费很大的资源,且不一定成功,如果新的需求不断变化,往往没有时间来重新开发这些代码。而这些遗留代码的功能没有完善的文档说明,甚至没有。 在此现状下,如何改善这些代码,将是考验程序员和一个团队的智慧。
接着,刘济华分析了对于遗留代码取舍的亲身经历。
1. 具有性能瓶颈的遗留系统——曾经遇到过一个应用服务,是C语言写的,周围其他系统都是采用Java开发,与此系统的交互都是采用非标准的协议完成,而且此系统处于比较核心的位置,但是维护非常复杂,不稳定,访问压力大是经常宕机,无法水平扩展,只能提高硬件设备水平等方式考虑。
在原有协议基础上开发,使其具有水平扩展能力,代价和开发一套标准协议的实现没有任何区别,往往会带了协议不够完善所产生的问题。于是,我们为其开发新的标准协议,WS,MQ等等,然后在标准协议上负载均衡实施水平扩展,非常方便。
随着后来的发展,此遗留代码也慢慢被新开发系统取代,期间经历了新系统和旧系统同时存在,此时新系统未完全具有旧系统的全部功能,这部分功能还是使用旧系统完成,只不过在原有均衡负载层多了层查找和分配的,后来到旧系统完全取代。此过渡还算平顺。
2. 功能性改造型系统——大多数就是这种系统,保留的话,代码极其复杂,维护麻烦,丢弃的话,无法一夜之间写出新的系统。曾经接手了一个开发失败的项目,包括代码和文档都未完善,如果重新来过,终究不划算,后来在此系统上进行改造,特别是花了大量时间写单元测试,保证代码测试覆盖率极高,这样一边熟悉代码,一边重构代码,系统的健壮性发生根本改变,前提是有时间。这只是特例。很多时候遇见的系统,同样测试代码很少,在添加新的功能和修复错误(Bugs)时,为这些能够接触到的代码完善测试,新代码必须测试覆盖率必须很高,经过4个月,,此系统代码覆盖率已经达到50%以上。以后的迭代开发越来越快。
那么如何在在遗留代码上编程呢?刘济华觉得首先要找到代码修改点:
遗留系统代码往往测试少,或者没有,导致软件开发者对软件发布没有信心。但是为所有的遗留代码写单元测试,初始代价非常高,在添加一个很小的功能时,并没有时间大动干戈。
如何找切入点呢?刘济华总结了几种情况:
在修改时需要一些技巧,其中包括:
①找测试方便、改动较小的方式来修改遗留代码。
②重构在一个类中那些重复的方法,并且保证其健壮性。
③为依赖的具体类提取新的接口,并使用注入依赖技术,使得测试更加容易,不管是使用Mock tool还是自己编写Mock对象,都会非常容易测试。
比如:
1
2
3
4
5
6
7
8
9
|
class
Manager{
...
public
void
kickOff(){
...
DoSomething doSomething =
new
DoSomething(...);
doSomething.doSomething(objects);
}
...
}
|
可改为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
Manager{
...
DoSomething doSomething;
public
void
setAction(Action doSomething){
this
.doSomething = doSomething;
}
public
void
kickOff(){
...
doSomething.doSomething(objects);
}
...
}
interface
Action {
void
doSomething(List marks);
}
|
④尽量使测试的范围缩小在受修改影响的类中,对类中的改动进行全面测试。保证每处修改完全测试,保证测试类减少。
⑤类之间交互的代码重构,如果这些交互仅在修改的代码之中,只要保证修改的代码完全测试即可。而对于那些可能影响此时其他不需要进行修改代码的类,可以先放下,为其创建新的方法,在此次修改和以后修改中,使用和重构新的方法。对于老的方法,等到以后代码覆盖率提高,能够覆盖所有此类交互方法的代码时,重构此方法,这是你会发现,修改很简单,并且如果修改错误,或者不能处理极端的逻辑,也会和容易找出问题所在。
⑥努力汲取业务逻辑知识。
⑦《修改代码的艺术》(Michael Feathers 著)建议找到切入点(Inflection Point),往往我们找的点很多,每一次修改都可能不一样,为此花很多代价找寻,还不如直接进入修改,找出最佳的修改方式避免代码过度重构和修改,减少影响,这才是有有实践价值和有意义的。
如何保证质量呢?刘济华建议: