美文网首页TDD(测试驱动开发)读书@IT·互联网
做好一点可让遗留代码起死回生

做好一点可让遗留代码起死回生

作者: 心水 | 来源:发表于2017-05-18 23:52 被阅读386次
    修改遗留代码的艺术

    对于程序员来说,最怕也是最不愿意做的一件事情就是修改遗留代码。因为大部分情况下遗留代码是人家写的代码,又烂又长,晦涩难懂,在不能完全读懂代码的情况下修改代码,是非常危险的。

    在《修改代码的艺术》这本书里,作者是这么定义遗留代码的:

    遗留代码是没有测试守护的代码。

    为什么这么说呢?因为代码会随着需求变更和时间推移而慢慢变坏,如果想一直保持代码的整洁,就必须持续地重构代码以改善代码的结构与设计,就好比我们要保持房间的整洁,就必须每天整理房间,否则房间肯定会变得乱七八糟。而持续重构的前提条件是要有测试来保证我们所做的改动不破坏已有代码的逻辑。所以,如果没有测试守护,我们就不敢重构,代码就会逐渐变成遗留代码。

    那有什么方法能让遗留代码起死回生吗?

    在修改遗留代码的时候,设法让代码以最小的成本运行起来,并在最短的时间内给出反馈。

    讲个故事吧,我们公司的小U刚接手一个遗留系统,这个系统会接收另一个系统发出的订单,数据传输是通过FTP,格式是XML,如下图所示,通过Scheduling Server,系统每隔5分钟会读取FTP上的文件,然后根据解析出来的内容创建或者更新订单,并把订单结果存储到数据库中。


    legacy system

    在小U还不太熟悉代码的时候,就接到一个需求,需要在订单接口中加入一个新信息“remark”,于是小U花了一些时间研究代码,并成功找到了读取FTP文件和处理订单的代码:

    public class ReceiveOrderMessageTask {    
        public void execute() {
            connectToFtp(readFTPConfig());
            List<String> fileContentList = readAllFiles("/in/orders");
            for (String fileContent : fileContentList) {
                OrderMessage orderMessage = JAXB.unmarshal(fileContent, OrderMessage.class);
                new ReceiveOrderTransaction(orderMessage).execute();
            }
        }
    }
    

    小U一开始还觉得挺幸运的,因为这段代码写得还算简单明了,ReceiveOrderMessageTask每五分钟会被SchedulingServer触发一次,读取FTP上的文件内容,然后把内容转成订单信息写入数据库,但是当小U想跑这段代码的时候,发现事情并不那么简单,小U必须先完成以下5个步骤:

    1. 在本地环境中配置好Scheduling Server并把它启动起来;
    2. 在本地环境中配置好Legacy System并把它启动起来;
    3. 找到本地环境连接的是哪个FTP,并往FTP上扔一个正确的订单文件;
    4. 等5分钟;
    5. 找到本地环境连接的是哪个数据库,在数据库里查看并验证订单是否正确生成;

    对于新手小U来说,这个过程一点都不简单,小U花了整整一个下午才把整个流程跑通,事后小U觉得非常不值得,因为他花了一下午只是为了把这段代码跑起来。痛定思痛,小U想清楚了问题的本质,其实核心逻辑就是解析XML文件并生成订单,这些代码和Scheduling Server,FTP甚至是数据库半毛钱关系都没有。那怎么才能在不搭建任何外部环境的情况下就把代码跑起来呢?小U把解析xml到java object的代码移到ReceiveOrderTransaction中并写了下面这个测试:

    public class ReceiveOrderMessageTest {    
        @Test
        public void testReceiveOrderMessage() {
            String xml = "<?xml version = 1.0 encoding = UTF-8"> " +
                                 "<OrderMessage> " +
                                 "    <orderNo>520131415</orderNo> " +
                                 "    <remark>unit tests can cure legacy code</remark> " +
                                 "    ... " +
                                 "</OrderMessage>"
            Order order = new ReceiveOrderTransaction(xml).execute();
            assertEquals("unit tests can cure legacy code", order.getRemark());
        }    
    }
    

    有了这个单元测试,核心代码1秒钟内就跑起来了,小U在5分钟之内就很有信心地完成了所有代码的改动,事后小U特别兴奋,他说:

    “如果以后再有新人做订单接口的改动,一定会很轻松,现在我虽然只检查了订单上remark信息是否是正确的,接下来我会逐步补充订单其它关键信息的检查,这样新人也不用担心他们的改动会破坏已有的逻辑了。随着这样的测试越来越多,大家会更通过重构逐步改善既有代码的设计,通过持续的改善,遗留代码一定会起死回生的!”

    总结一下,要让遗留代码起死回生,你就要让代码以最低成本运行起来,并在最短的时间内给出有意义的反馈,在做好这一点的基础上,你才可能持续地重构代码,保持代码的整洁,改善代码的设计。

    相关文章

      网友评论

      • 0dbde0b5b8b2:关键得解藕
        心水: @yuyong0621 解耦和重构是小步和持续的
        0dbde0b5b8b2:@葛亮 解藕需要有规划,得得重构
        心水: @yuyong0621 嗯嗯,高内聚低耦合
      • 阿群1986:题外话:遗留代码有一定价值,但不一定真的那么有价值。欢迎加入代码考古爱好者俱乐部:joy:
      • 心水:第一点见最新代码:)
      • Cuist:一你在测试里重复了产品代码,若产品代码改了,谁来保证测试代码的改动呢?二原来的代码确实是未考虑可测的,一开始就TDD应该怎么改?三按照遗留代码那本书的方案,应该怎么改?

      本文标题:做好一点可让遗留代码起死回生

      本文链接:https://www.haomeiwen.com/subject/uirjxxtx.html