1. 引言
在当今快速发展的信息技术时代,软件系统的复杂性不断增加,代码质量成为影响软件系统性能、可维护性和可扩展性的关键因素。代码异味(Code Smell)是违反设计标准,低质量的代码段[1],代码异味作为软件开发过程中的一个常见问题,它虽然不直接导致代码错误,但却是代码结构或设计存在缺陷的明显标志[2]。这些缺陷可能会降低代码的可读性和可维护性,增加系统维护的难度,甚至影响软件的性能[3]。因此,及时识别并重构代码异味对于提高软件质量至关重要[4]。
传统的代码异味检测方法主要依赖于人工检查或工具辅助。人工检查依赖于开发人员的经验和直觉,效率较低,且容易受到主观因素的影响[5]。工具辅助虽然可以提高效率,但现有的检测工具在一致性、准确性和泛化能力方面存在一定的局限性[6]。在过去的十年中,移动软件应用程序的使用在全球范围内呈现指数级增长。据报道,Android是一款最受欢迎的移动操作系统,市场份额约占80% [7]。随着Android操作系统的普及,针对Android应用程序的代码异味问题也日益凸显。关于面向对象代码异味的检测及重构的研究很多,但对于Android代码异味的研究却很少[8],Android代码异味的检测仍然是一个研究热点问题[9]。为了克服这些挑战,本文提出了一种基于机器学习的缓慢循环异味检测方法。近年来,机器学习技术因其强大的模式识别能力被广泛应用于代码异味检测中,展现出高效、可扩展的优势[10]。本文的主要贡献在于构建了一个高质量的数据集,并采用了五种不同的机器学习算法来检测Kotlin语言中的缓慢循环异味。这些算法包括决策树(C4.5)、朴素贝叶斯(NB)、逻辑回归(LR)、随机森林(RF)和基于规则的归纳算法(JRip)。通过实验验证,展示了这些算法在检测缓慢循环异味方面的有效性,并对比了它们在查准率、查全率和F1值等评价指标上的性能。
2. 相关工作
2.1. 代码异味
代码异味(Code Smell)是一个软件开发术语,由Martin Fowler最早于1999年提出[11],用来描述代码中可能存在问题的代码段。这些问题并不代表代码有错误,而是表明代码的结构或设计可能存在缺陷,会影响代码的可读性、可维护性、可扩展性以及性能[3]。代码异味通常是由于设计缺陷或不良编码习惯导致的。
代码异味分为面向对象代码异味与Android特有代码异味,Fowler曾使用代码异味表示在面向对象编程中出现的一些不良编程实践,这类代码异味被研究者们称为面向对象代码异味(Object-Oriented Code Smells) [12]。Android特有代码异味是指在Android应用程序开发中出现的与Android平台紧密相关的代码问题,也称为Android代码异味。Riemann等人提出了针对Android移动应用程序的30种代码异味的目录[13]。这些代码异味可能与Android的架构、API、组件生命周期等因素有关,它们可能会导致应用程序的性能问题、内存泄漏、电池消耗过快或其他维护难题。
2.2. 缓慢循环异味
缓慢循环(Slow Loop,简称SL)是一种Android特有代码异味,在循环中执行耗时操作,延长了程序的执行时间而导致程序整体性能下降,因此被称为代码异味,SL在Android应用程序中的具体表现为传统for循环语句。缓慢循环异味在Android应用程序中出现频率较高,对程序的影响主要有以下三方面:一、性能问题:低速处理数据会显著降低程序的执行效率,尤其是在需要多次迭代的循环中,每次迭代的延迟都会被放大,导致整体性能下降。二、可维护性问题:包含缓慢循环异味的代码通常难以理解和维护,循环中的耗时操作可能使得代码逻辑变得复杂,增加了代码维护的难度。三、资源浪费问题:缓慢循环异味可能会导致系统资源(如CPU、内存)的浪费,因为耗时操作会占用资源而不释放,从而影响其他程序或操作的执行。缓慢循环异味的重构方法是将传统for循环转变为效率更高的for-each循环[14]。图1所示为缓慢循环异味实例。
Figure 1. Instance of slow loop
图1. 缓慢循环异味实例
2.3. 基于机器学习的代码异味检测方法
虽然目前现有的代码异味检测工具表现良好,但曾有研究表明其在应用实践中有很大的局限性。检测工具的一致性较低,这意味着不同的检测工具对于相同的代码异味可能会出现不同的检测结果[15]。并且当前大多数的检测工具都需要指定阈值,阈值的选择强烈影响检测工具的性能,检测结果可以被开发人员主观地感知和解释。为了克服这些限制,机器学习开始被用于检测代码异味。
机器学习具备强大的模式识别能力,可以高效地分析和处理大规模代码库[5]。此外,机器学习具有从数据中持续学习和改进的能力,这意味着随着时间的推移,它们的检测能力会变得更加精确。机器学习还能综合考虑代码的多种特征,提高检测的准确性,并能够泛化到新的代码上,识别出新的或未知的异味。它还可以扩展以适应新的编程语言或代码异味类型,并辅助开发人员做出修复决策。总的来说,机器学习为代码异味的检测提供了一种高效、可扩展和自动化的解决方案。Fontana [15]等人应用16种不同的机器学习模型检测4种面向对象代码异味(数据类、长方法、依恋情结、大类),大多数模型性能表现良好。现有的研究大多集中在面向对象代码异味的检测,对Android特有代码异味的检测研究较少,所以本文采用五种机器学习算法即决策树(C4.5)、朴素贝叶斯(NB)、逻辑回归算法(LR)、随机森林(RF)和基于规则的归纳算法(JRip)检测一种出现频率较高的Android特有代码异味——缓慢循环异味。
3. 数据集构建
在机器学习应用于代码异味检测的领域中还未有公开的数据集可供使用,为了解决这个问题以及保证数据集的质量,本文按照language:kotlin stars:>=500 and size:>=1000 and pushed:>=2023-01-01 android (使用Kotlin语言,星标至少为500,应用程序规模至少为1 MB,并且在2023年1月1日之后有更新的Android应用程序)的标准在GitHub上筛选出100个Android移动应用程序。GitHub是全球最大的代码托管平台之一,被广泛用于软件开发和项目管理。星标数代表程序的认可度和流行度,星标数越大代表程序的受欢迎程度越高。更新时间是衡量项目活跃度的一个重要指标。一个经常更新的项目通常意味着它正在积极维护和发展中,代表程序质量较好。表1所示为部分移动应用程序的信息。
Table 1. Information of some mobile applications
表1. 部分移动应用程序的信息
项目名称 |
Star |
Release |
Contributor |
应用领域 |
版本 |
MEGA |
1.4k |
138 |
44 |
访问云存 |
V11.6 |
Home Assistant |
2k |
1437 |
120 |
智能家居 |
V1.5 |
WordPress |
2.9k |
653 |
198 |
播客 |
V24.1 |
Flexbox |
18.2k |
25 |
28 |
空间布局 |
V3.0.0 |
Kickstarter |
5.7k |
89 |
34 |
众筹平台 |
V3.18.0 |
ACRA |
6.2k |
53 |
9 |
程序监控 |
V5.11.3 |
Muzei |
4.6k |
23 |
30 |
动态壁纸 |
V3.4.1 |
Task |
3.3k |
222 |
221 |
任务管理 |
V13.8.1 |
由于目前没有工具可以对Kotlin应用程序进行正负样本自动提取,本文采用手动分析,构建正负样本。构建好的样本集为代码段,而机器学习模型的输入需为数字向量,因此本文使用工具Tokenizer进行代码转换。Tokenizer是自然语言处理(NLP)中的一种关键工具,它的主要作用是将文本字符串分割成一系列的标记(单词,特殊符号等)。图2所示为Tokenizer标记示例。
Figure 2. Instance of slow loop
图2. 缓慢循环异味实例
转换后的数字向量包含代码段的全部信息,但可能存在数据缺失或者存在异常值等情况,需采用数据归一化、特征选择等进行数据预处理。经统计及分析,80%以上的特征值不超过125个,即剔除特征数超过125个的样本,对于特征数小于125的样本用零向量进行补充。经过上述过程,最终得到一个包含7000个样本的数据集,其中正样本(含有代码异味) 3600个,负样本(不含有代码异味) 3400个,正负样本个数基本平衡。图3所示为数据集格式。
Figure 3. An example of dataset format
图3. 数据集格式
4. 机器学习算法
本文采用五种机器学习算法检测缓慢循环代码异味,分别是决策树(C4.5)、朴素贝叶斯(NB)、逻辑回归算法(LR)、随机森林(RF)和基于规则的归纳算法(JRip)。
4.1. 决策树算法
决策树算法是一种模拟人类决策过程的机器学习方法,它通过一系列的问题将数据分类或预测。决策树直观易懂,不需要复杂的预处理步骤,并且可以处理数值型和类别型数据。它们也能够处理多输出问题,并且对于非线性关系和数据中的交互关系建模效果很好。这种算法的核心是特征选择,即选择最能代表数据特征的属性来进行决策。决策树的构建过程是从根节点开始,根据数据集中的特征及其值,递归地分割数据,直到满足特定的停止条件,如达到最大深度、节点中的样本数量低于某个阈值,或者所有样本都属于同一类别。在构建决策树时,算法会评估每个特征的每个可能的分割点,然后选择能够最好地将数据分类的特征和分割值。这个过程会递归地重复,直到每个分支的叶节点都不能再进一步分割。决策树的每个叶节点代表一个最终的决策,例如在分类问题中的类别标签。
4.2. 朴素贝叶斯算法
朴素贝叶斯算法是一种基于贝叶斯定理的简单概率分类器,算法的核心思想是:给定一个样本,算法会计算样本属于每个类别的概率,并将样本分类到具有最高后验概率的类别。贝叶斯定理提供了一种计算后验概率的方法,它依赖于先验概率、似然概率和边缘概率。在朴素贝叶斯算法中,先验概率是指在不考虑任何特征影响下,样本属于某个类别的概率。似然概率是指在已知类别的情况下,特征的概率分布。算法的训练过程相对简单,主要是计算每个类别的先验概率以及每个特征在各类别下的条件概率。在分类时,对于给定的样本,算法会计算样本属于每个类别的后验概率,后验概率最高的类别即为预测结果。
4.3. 逻辑回归算法
逻辑回归算法是一种广泛应用于二分类问题的统计模型,逻辑回归模型基于线性回归,但它的输出经过逻辑函数(Sigmoid函数)转换,使其值域在0到1之间,表示为特定类别的概率。逻辑回归的一个重要特点是它的输出可以被解释为概率,这使得模型的预测结果具有直观的解释性。
4.4. 随机森林算法
随机森林算法通过构建多个决策树并将它们的预测结果进行整合来提高模型的性能和准确性。随机森林中的“随机”指的是在构建决策树的过程中引入随机性,以增强模型的泛化能力并减少过拟合。在随机森林中,每棵树都是在数据集的一个随机子集上训练的,这个子集是通过有放回抽样(即自助采样)得到的,这种方法也称为bootstrap sampling。此外,随机性还体现在特征的选择过程中:在每个决策节点,不是考虑所有可能的特征,而是随机选择一部分特征,然后从中选择最佳分裂特征。
4.5. 基于规则的归纳算法
基于规则的归纳算法是一种传统的机器学习方法,它从数据中学习并产生一组规则来对新实例进行分类或预测。算法的核心目标是发现数据中的模式和关联,并将这些模式表达为规则。在基于规则的归纳算法中,数据通常被表示为实例的集合,其中每个实例由一组属性和相应的值组成。算法的任务是找出决定每个实例类别的最重要属性和属性值。这些规则可以通过不同的策略来发现,包括归纳逻辑编程、决策树学习和基于遗传算法的学习方法。
5. 实验验证
5.1. 实验设计
本文提出的基于机器学习的缓慢循环异味检测方法流程图如图4所示。首先构建数据集,数据来源于GitHub,按照一定的标准筛选Kotlin编写的移动应Android用程序,通过手动分析提取正样本及负样本。由于传统机器学习模型的输入是数字,而样本集是代码段,所以需要将提取出的正负样本经过Tokenizer转换为数字向量,然后进行数据预处理得到数字向量样本集输入本文所采用的五种传统机器学习分类器中,得到代码异味检测结果,并通过模型评价指标来评价不同分类器的性能。
Figure 4. Overview of the code smell detection method
图4. 检测方法流程图
5.2. 模型评价指标
本文使用查准率(Precision)、查全率(Recall)和F1值(F1-score)来评估不同传统机器学习模型的性能,公式如下所示:
(1)
(2)
(3)
查准率又称为精确率,查全率又称为召回率,这两个指标衡量的均是模型预测为正类别中实际为正类别的比例,即表示模型预测正确的正例占所有预测为正例的比例。TP (True Positives)是真正例的数量,即正确预测为正类别的样本数;FP (False Positives)是假正例的数量,即错误预测为正类别的样本数。FN (False Negatives)是假负例的数量,即错误预测为负类别的正类别样本数。F1值是查准率和查全率的调和平均数,取值范围在0到1之间,值越高表示模型的性能越好。F1值提供了一种在查准率和查全率之间取得平衡的方法,特别适用于那些正负类别样本不平衡的情况。
5.3. 实验结果分析
本文通过实验验证了基于机器学习的缓慢循环异味检测方法的有效性。实验结果分析部分将详细讨论不同机器学习模型在检测缓慢循环异味时的性能表现,并探讨影响模型性能的可能因素。本文采用了五种机器学习算法:随机森林(RF)、基于规则的归纳算法(JRip)、逻辑回归(LR)、决策树(C4.5)和朴素贝叶斯(NB)。检测结果下表所示。
Table 2. Detection results of different classifiers
表2. 不同分类器的检测结果
分类器 |
查准率 |
查全率 |
F1值 |
RF |
0.995 |
0.995 |
0.990 |
JRip |
0.996 |
0.913 |
0.947 |
LR |
0.903 |
0.898 |
0.892 |
C4.5 |
0.901 |
0.864 |
0.881 |
NB |
0.850 |
0.800 |
0.793 |
从表2可以看出,随机森林(RF)的性能最优,在F1值和查全率上均最高,并且在查准率上也表现出色,达到了0.995,这表明RF在预测准确性和覆盖率方面都非常优秀,且在查准率和查全率之间取得了良好的平衡。基于规则的归纳算法(JRip)在查准率上表现最佳,达到了0.996,但在查全率上稍低,为0.913,这可能意味着JRip在预测正样本时较为严格,导致一些真正的正样本被误判为负样本。逻辑回归(LR)和决策树(C4.5)在查准率和查全率上的表现较为均衡,但相比于RF和JRip,它们的性能略低,F1值分别为0.892和0.881。这表明这两种算法在处理此类数据时可能存在一定的偏差,需要进一步优化。朴素贝叶斯(NB)算法的表现相对较弱,查准率和查全率分别为0.850和0.800,F1值为0.793。这可能是因为NB算法对特征的独立性假设在实际数据中并不总是成立,导致其性能受限。
总体而言,基于机器学习检测缓慢循环异味可行且性能较好,其中随机森林表现最佳,基于规则的归纳算法如JRip也显示出较高的查准率。但所有算法在查全率上都有提升的空间,这可能需要进一步的研究和优化。此外,数据预处理的质量和模型的复杂度也是影响性能的重要因素。未来的研究可以探索更复杂的模型或集成方法,以进一步提高检测性能。
6. 结语
本文提出一种基于机器学习的缓慢循环异味检测方法,采用了五种机器学习算法即随机森林(RF)、基于规则的归纳算法(JRip)、逻辑回归(LR)、决策树(C4.5)和朴素贝叶斯(NB),为提高Android应用程序的代码质量提供了一种新的解决方案。实验结果表明,采用的机器学习算法能够有效地检测出代码中的缓慢循环异味,尤其是随机森林算法,在查准率、查全率和F1值上均表现优异。这证明了机器学习技术在Android代码异味检测领域的应用潜力。未来的工作可以集中在优化现有算法,以提高查全率,减少误报。此外,可以考虑引入深度学习等更先进的技术,以进一步提高检测的准确性和效率。同时,研究如何将这些方法集成到现有的软件开发流程中,实现自动化的代码异味检测和修复,也是未来工作的重要方向。
基金项目
黑龙江省高等教育教学改革研究项目。
NOTES
*通讯作者Email: bianyu79@163.com