APP下载

代码重构方法在敏捷开发中的应用

2017-05-12鲜茜

现代计算机 2017年9期
关键词:单元测试代码重构

鲜茜

(四川大学计算机学院,成都 610065)

代码重构方法在敏捷开发中的应用

鲜茜

(四川大学计算机学院,成都 610065)

软件产品由于互联网蓬勃发展而以井喷之势持续递增。许多企业选择敏捷开发模式来节约时间成本。同时,需要对软件进行不断地完善和改进,这导致软件越来越复杂,维护成本增加。因此,代码重构显得十分重要,通过参考大量文献和依托项目实例,总结优化代码的一些改进方法,并以结果展示代码重构后能提高其性能,使软件更易于理解和维护。

重构;敏捷开发;改进方法

0 引言

科技社会日新月异,互联网行业突飞猛进,许多互联网公司如雨后春笋般崛起。在面临行业竞争,需要提升产品的功能多样性、易用性、安全性、用户体验等复杂需求下,企业往往采用敏捷开发,这种开发技术的优点不言而喻,但缺点在于小组成员间工作较为独立,各自在代码风格、逻辑结构方面有较大差异。软件产品随着时间的推移,必须不断地修改原有的代码、增加新的功能。为了实现变更,不可避免地要违反最初的设计构架。一段时间以后,软件的架构就会千疮百孔。代码以指数级数量增加,发现的代码错误越来越多,系统变得很难维护,间接说明代码存在许多问题。例如:可读性差、冗余、代码依赖性强、自动测试代码更新不及时、维护困难。

这个现象带来了科技的革新:代码演化技术、代码自动化生成、测试驱动、形式化验证、代码重构与优化等方面的研究。其中,代码重构技术十分重要,它是优化功能代码的核心技术,不改变系统的外部功能,只对内部的结构进行重新的整理,能够在较大程度优化代码,使系统对需求变化具有自适应能力。

通过实际项目中代码重构经验,分析对比重构前后代码性能、代码复杂度等参数。总结其中有效的优化方法,保证功能工作的情况下,使软件得到改进。

1 相关工作

Martin Fowler清楚揭示了重构过程,解释重构的原理,并指出何时何地你应该开始挖掘你的代码以求改善[1]。陈林等人针对现有泛化关系重构方法在处理泛型程序时不考虑参数化类型的约束,会破坏程序类型正确性的问题,提出了一组泛型Java程序的类型约束规则,可以在与程序代码规模成线性关系的时间内实施重构,并有效地保证程序的类型正确性[2]。Kataoka等人提出一种定量评价方法来衡量程序重构的可维护性增强效果。专注于耦合度量来评估重构效应[3]。Van等人提供了一组具有“坏气味”的代码,之后给出了测试的集合重构去除这些问题代码。最终,通过一个简单程序的修改,测试重构解释的集合证明如何克服这些问题[4]。Du等人分析在哪些特定条件下如何重构操作耦合/凝聚力特征,以及如何识别重构的这些特征是否得到改善,最终在一个开放源码软件系统验证相关改进和适用性[5]。

2 重构方法概述

根据目标管理SMART原则,制定重构计划。需要考虑重构所需时间,具体地为每个小目标设定一个合理的最后期限,并设置明确的度量来评估目标是否达成。采取结对编程有效策略,重构之前构建有效的测试来保证代码质量。将重构分为功能代码和测试代码两部分来进行。如图1所示,审视代码整体结构和风格,分析出依赖关系并统一风格,在重构之前进行回归测试。接下来对功能代码风格和业务逻辑进行重构,并每修改一次,进行回归测试,一直到所有结束。

图1 重构流程图

2.1 重构重点

①打破依赖明确的关系

②构建坚实的测试

③优化代码,重新审视遗留缺陷

2.2 重构步骤

在实际工程项目中,代码库一直在持续集成服务器运行着,所以需要先从源代码干线拉出一条分支进行重构。这样可以使重构周期限于非常小的范围,通常为1或2个类;改进类设计时不会涉及业务逻辑,修改一个类之后,需要用自动化测试工具进行功能测试,直到测试通过;周而复始继续修改类,直到所有类都更改,保证功能不受影响后,提交回代码库主干线。

3 实现及方法总结

3.1 功能代码优化

(1)有状态的服务对象

●症状

一些服务对象是有状态的,因为它们会从请求对象那里提取数据。代码为每个服务对象将请求对象转换成一个字段,并为每个请求创建新对象。当系统负载很高时会对JVM(Java虚拟机)造成强工作负载来分配内存和垃圾回收。让所有的服务对象线程变得不安全。

●解决方法

打破依赖,有一个明确的数据和逻辑的分离,让所有服务对象无状态,状态驻留在请求对象本身。所有的服务方法的请求对象作为输入参数,这样使得每个服务对象可以一次创建并为多个请求所重用。

图2 类重构流程图

(2)类关联

●症状

类与类之间有继承关系、泛化关系、依赖关系、关联关系、聚合关系、组合关系六种关系,这些关系分类较细,有时往往会对此认识不清晰。没有明确的泛化关系,而依赖关系是由调用者从静态工厂方法和获取使用之前初始化。而且代码中存在工厂模式的错误使用导致代码冗余、代码层次多余、结构紊乱等问题。很难阐述代码变化的影响,而且容易创建一个循环引用关系。

●解决方法

通过为依赖接口创建字段,使依赖关系显式化。封装调用者的类和实现函数的功能代码,实现高内聚低耦合。

(3)对象创建

●症状

对象实例是根据每个请求创建,即使在同一类的不同方法中,也会重复创建相同的依赖对象实例。随意的创建对象会增加了JVM的不必要的工作负载,并可能导致性能问题,从而整个产品性能较差,用户体验不佳。

●解决方法

除了请求对象之外,所有对象实例都由Spring(Java的一种框架)在应用程序启动时创建一次,并重复用于不同的请求,只有在必要时才创建一个新对象,对象生命周期应该由框架管理。依赖对象是类的字段,使用Spring框架创建对象实例,使对象单例,然后使用Spring框架注入依赖对象实例。

(4)功能函数

●症状

函数是程序中最基本的功能结构,是一个可以从程序其它地方调用执行的语句块。函数通常只完成一个功能,当函数代码行数数量庞大,缺少战略异常处理和日志记录。会使得代码难以阅读,而且难以理解,查错和维护变得困难。函数嵌套层次比较深时,内部调用外部代码时常常会引发错误调用。

●解决方法

良好的软件系统设计需要将函数设计为实现单一功能,并且有一个自顶向下的抽象层。缩短代码函数以便于阅读、理解和维护代码。尽可能调用已有成熟的开源编程实现的函数。

(5)异常

●症状

异常处理功能提供了处理程序运行时出现的任何意外或异常情况的方法。异常处理使用 try、catch和finally关键字来处理可能未成功的操作和失败,以及在事后清理资源。通过异常处理,可以对用户在程序中的非法输入进行控制和提示,以防程序崩溃。在项目代码中使用超级异常包装各种错误代码,范围跨度太大,不能较好地适用于不同的异常情况,同时导致错误处理和日志记录几乎发生在每种方法中,十分冗余和低效。

●解决方法

为不同的情况定义不同的异常类型,通过try{} catch()子句中的类型处理异常。并且仅使用运行时异常。如果错误处理的唯一操作是日志记录,则在每个模块的入口点只执行一次。设计时,定义异常需考虑层次、细分情况和系统产品界面友好。

程序中的关键代码如下:

3.2 测试代码优化

自动化测试是代码重构的先决条件和重要步骤。没有构建完备的自动化测试环境,即使代码优化完成后,也无法验证是否每一个修改动作是否是正确的。因此,构建和优化自动测试代码也十分必要。

(1)单元测试设计与风格

●症状

通常而言,一个单元测试是用于判断某个特定条件下某个特定函数的行为。然而,在项目代码中单元测试代码数量巨大,一个案例测试了多个功能,单个案例中包括多个“assert”语句,显得十分冗长。在单元测试代码中没有“Given-When-Then”代码风格结构,易读性差。

●解决方法

每个单元测试按照“Given-When-Then”代码风格写,使逻辑结构清晰,提高易读性。每个案例只专注测试一个函数,减少测试代码行数。

程序中的关键代码如下:

●症状

mock测试是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。伪依赖的实现在单元测试中被用作mock,有时测试目标本身被部mock。更糟的是使用Java反射机制,并且当设置mock对象时,方法名称被硬编码为字符串。一半的测试代码是关于设置mock,而不是关于测试开发代码本身。

●解决方法

所有的mock由mock框架动态创建的,没有实际的mock类代码。测试目标是真正的实现代码,注入所有mock依赖项。使用代理对象、模拟对象和辅助对象来隔离网络,数据库,文件和用户接口。设置mock时使用智能默认设置,可以减少mock代码。不要用硬编码设置方法名,mock最多用来测静态方法。

(3)覆盖率

●症状

测试覆盖是对测试完全程度的评测。测试覆盖是由测试需求和测试用例的覆盖或已执行代码的覆盖表示。测试代码中路径覆盖似乎很好,但事实上一些边界和特殊条件并没有测试。一些测试用例没有验证到任何功能,它们只是用来制造一个不错的覆盖率。

●解决方法

删除无用的测试保证测试单元测试用例有用且有效。不要为了覆盖率在单元测试中制造空的测试函数。即使覆盖数量已经达到目标,继续优化边界和异常测试。

4 重构结果分析

表1展示了项目其中三个模块代码重构前后一些参数的对比,可以清晰的看到,代码行数、方法复杂度和类复杂度有在一定程度上减少,有效地减少了软件容量。而单元测试覆盖率和事务成功率的增加间接说明软件质量得到保证、性能提高。

表1 相关参数比较

5 结语

一方面,软件质量得到巨大的改进,具体体现在冗余代码归类、抽象提取和删除,结构重新设计、减少时间复杂度,使代码易读,便于维护。另一方面,通过项目实践增强对重构代码的理解,掌握了如何结合实际情况构建重构的方法。维护工作减少,参与重构工作的人员的业务能力提高,对代码逻辑有了清晰的认识。

未来研究设计重构驱动开发的方法及工具,选择最优架构方案,避免多次修改代码,破坏整体架构以及付出巨大的人力开销。探索更多地使用于不同规模、用途的软件系统的代码重构方案,以便提高工业界生产效率,保证产品质量。

[1]Martin Fowler.Refactoring:Improving the Design of Existing Code[M].America:Addison-Wesley Professional,1999.

[2]陈林,徐宝文,周晓宇等.一种基于类型约束的泛型Java程序重构方法[J].电子学报,2007:35(s2):185-191.

[3]Kataoka Y,Imai T,Andou H,et al.A Quantitative Evaluation of Maintainability Enhancement by Refactoring[C].Software Maintenance,2002.Proceedings.International Conference on.IEEE,2002:576-585.

[4]Van Deursen A,Moonen L,van den Bergh A,et al.Refactoring Test Code[C].Proceedings of the 2nd International Conference on Extreme Programming and Flexible Processes in Software Engineering,2001:92-95.

[5]Du Bois B,Demeyer S,Verelst J.Refactoring-Improving Coupling and Cohesion of Existing Code[C].Reverse Engineering,2004.Proceedings.11th Working Conference on.IEEE,2004:144-151.

作者简介:

Application of Code Refactoring in Agile Development

XIAN Xi
(College of Computer Science,Sichuan University,Chengdu 610065)

Software products blow up the trend that continues to increase due to the vigorous development of the Internet.Many companies choose agile development models to save time costs.At the same time,the need for continuous improvement and improvement of software,which lead software become more and more complex as well as the maintenance costs increased.Therefore,the code refactoring is very important.Summarizes the improved methods of optimizing the code by referring to a large number of documents and relying on the project experience,and shows the improvement of performance with result makes the software easier to understand and maintain.

Refactoring;Agile Development;Improvement Methods

1007-1423(2017)09-0123-05

10.3969/j.issn.1007-1423.2017.09.028

,女,四川成都人,硕士,研究方向为软件质量保证与测试

2017-03-11

2017-03-20

猜你喜欢

单元测试代码重构
“双减”能否重构教育生态?
长城叙事的重构
高盐肥胖心肌重构防治有新策略
创世代码
创世代码
创世代码
创世代码
北京的重构与再造
一年级上册第五单元测试
一年级上册一、二单元测试