敏捷实践 | 代码是如何腐烂的

代码是如何腐烂的?这是一个很大的命题,因为这种腐化的代码样本可能会体现不同的特征。若要彻底总结,可能会又是一本《重构》。我自然没有这个能力和知识。好在有一个简便的说法,即可以诉诸于“破窗理论”的威力。无论多少坏味道识别,重构手法运用,提高代码质量的最佳实践,以及运用诸多甄别代码质量体征的工具,都仅仅限于“术”的运用而已。若未能在开发人员内心树立整洁代码的习惯,时时刻刻对各种代码臭味保持敏感,且具有一颗期待卓越代码之心,那么,随着项目的演进,时间的推移,代码最终还是会慢慢腐烂。

我曾经参与一个项目,在一次结对开发某个User Story时,从诸多测试代码(包括集成测试与验收测试)中,依然观察到了一些接近腐烂的代码坏味。这些代码虽然不是产品代码,但同样是我们交付工件的一部分。最关键之处在于:它让我察觉到一种危险的趋势,若不能及时扭转,可能会让代码陷入腐烂的泥沼。若能及时解决这些糟糕代码,其实仅仅需要一些简单的重构手法,付出几个小时时间即可。

首先是针对集成测试的数据准备。

我们要编写的集成测试针对Spring Batch Job,这些Job需要访问数据库,以验证Job的执行是否符合期望。我们发现在之前已有与Spring Batch Job相关的集成测试存在,并提供了访问数据库,以及启动、访问和停止Ftp服务器的功能。其中,与数据准备有关的功能放到单独定义的Fixture类中。这些Fixture是为特定目的编写的数据准备;可是,随着越来越多的Batch Job出现,有诸多集成测试都需要准备数据,慢慢产生了测试数据的重叠,逐步浮现出违背DRY原则的征兆了。

对于多数程序员而言,并非不重视重用,但多数却不愿意为了重用付出一些代价。例如针对一些具备差异性的功能,一些程序员更愿意使用Copy And Paste,然后再针对自己的需求对实现进行修改或调整。观察目前的一些集成测试,正是这样一些陋习导致的。

在这些集成测试中,使用了继承的方式来重用数据准备的功能。如下图所示:

在CustomerIntegratedDataFixture中,提供了相关方法实现了对Customer数据的创建。由于需要提供访问FtpServer的功能,因此又定义了CustomerIntegratedDataAndFtpPrepareFixture类,使其继承CustomerIntegratedDataFixture。它定义了startFtpServer()和stopFtpServer()方法,并在JUnit中,运用了@BeforeClass与@AfterClass标记,使其避免为每个测试启动和停止专有的FtpServer。

现在,我们编写的集成测试同样需要与Customer有关的数据,但并不需要Ftp功能。换言之,我们希望重用CustomerIntegratedDataFixture。目前似乎并没有问题。例如,我们可以让新增的测试直接继承CustomerIntegratedDataFixture。然而,就在同样的集成测试模块中,我们还发现了其他集成测试同样编写了自己的数据准备类。这些数据准备与Spring Batch Job无关,却同样提供了准备Customer数据的功能。存在的差异是它除了提供Customer数据外,还提供了依赖Customer的Consent数据。

我们没有着急去重用CustomerIntegratedDataFixture,因为我们察觉到代码会随着这种继承体系的延伸,会变得越来越难以重用。如上图的继承体系,使得数据准备与Spring Batch Job紧耦合了,同时又在CustomerIntegratedDataAndFtpPrepareFixture子类中引入了与Ftp有关的耦合,明显违背了单一职责原则。我们需要单独剥离出数据准备的类,它即可以作为超类被集成测试类继承,也可以通过组合的方式被继承了JobLauncherTestUtils的测试子类所调用。这符合Bridge模式的设计原则。因此,我们运用了“Replace Inheritance with Delegation”手法,对其进行了简单重构:

之后,我们对Customer和Consent对应的数据准备类进行了相应的重构与修改,使得这些数据的准备更为内聚,并去除一些不必要的重复,使之更容易被重用。

在这个例子中,代码的坏味道并不隐蔽,运用的重构手法也并不复杂,我相信大多数开发人员都具备这样的技能。然而,我们总是因为种种原因,对这种还不太严重的“破窗”风景视而不见。殊不知当我们开始对这种不够整洁的代码采取纵容态度时,就可能会是代码腐烂之始。一旦真正腐烂,就将积重难返,到了那时,我们就可能真正无能为力了。

你是否遭遇过这样的情形?面对一个承担了无数职责似乎无所不能的上帝类,它被无数多的Client调用,且又没有足够覆盖率的测试,你是否会产生心有余而力不足的感慨。这时的你,是否像一位奋战沙场,出生入死却无力挽回败局的将军,面对那汹涌而来占据压倒性优势的敌军,唯有对天长叹:“某有心杀贼,却无力回天啊!”

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2014-09-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏牛客网

阿里2019实习内推,五轮技术面+一轮HR面,Java岗面经

在牛客网上获取到很多知识和信息,现在反馈一波,希望能对广大找实习的同学有所帮助。 个人情况:EE方向渣硕,二月末内推了阿里集团某部门Java岗,约三周完成了所有...

4175
来自专栏企鹅号快讯

python程序员开发必备的5大工具,你用过几个?

随着python的火热,不少的程序员业余时间都会研究这门编程语言。 利用python开发,大牛用vim,接了2个显示器写python,气场甚是强大。 有些程序员...

2667
来自专栏人工智能LeadAI

数据清洗经验

平时习惯了在某些特定的数据集合上做实验,简单的tokenization、预处理等步骤就足够了。但是在数据越来越大的年代,数据清洗越来越重要,也越来越复杂。看到P...

3924
来自专栏阮一峰的网络日志

程序员小测试:保守派 vs 自由派

最近,我在阅读 Steve Yegg 的文集《程序员的呐喊》。 ? 这是一本非常有趣的书,里面甚至包含了一个小测试(原文),区分一个程序员到底是保守派还是自由派...

3026
来自专栏钱曙光的专栏

一周极客热文:200 行 C 代码编写你的第一个垃圾收集器

一名程序员在许多事物缠身,心里烦乱的情况下如何排解呢?Google Dart团队的一名工程师通过编写一个“垃圾收集器”来调整自己,而且起到了一个非常好的效果,但...

2139
来自专栏java工会

学java就两个问题

2218
来自专栏云计算D1net

干货:如何计算用户行为大数据

用户行为类数据是最常见的大数据形式,比如电信的通话记录、网站的访问日志、应用商店的app下载记录、银行的账户信息、机顶盒的观看记录、股票的交易记录、保险业的...

3515
来自专栏架构专栏

Java程序员年薪40W,他1年走了别人5年的路(技术提炼建议收藏)

介绍一下,这一次笔者笔下的这位大牛,lison,复旦大学工程硕士,专注技术十年,产品控、代码控,拥有丰富的项目经验,主持研发了多个成功上线的大型互联网项目。热爱...

1801
来自专栏coding

写下这行代码时,只有我和上帝知道是怎么回事01.烂代码的路径依赖02.对于烂代码应采取零容忍03.代码规范的重要性04.文档的重要性

"算了,这里的代码有说不清的玄机,重构相当于在给自己挖更大的坑,还是按照原来的写法吧..."

893
来自专栏程序员八阿哥

王老板Python面试(3): 一个初级python web后端开发工程师的面试总结

之前一直在做C++的MFC软件界面开发工作。公司为某不景气的国企研究所。(喏,我的工作经验很水:1是方向不对;2是行业有偏差)。

1892

扫码关注云+社区

领取腾讯云代金券