1. 引言
软件系统需要不断更迭,以保持其价值和竞争力。只有不断地更新,才能保证软件系统能够持续地满足用户需求和市场需求[1]。因此,高质量的源代码至关重要。高质量的源代码既有良好的可读性、可维护性和健壮性,同时又能够满足项目的需求并且易于扩展和修改[2]。然而,迫于时间和资源的压力,开发者不得不在开发过程中使用次优解决方案,因此引入了代码异味[3]。代码异味违反了基本的代码设计原则,降低了软件质量[4] [5]。一项实证研究表明,27%的维护问题是由代码异味引起的[6] [7],这已经成为了开发过程中的积压债务。出现在桌面应用程序中的代码异味被称为面向对象代码异味。因此,及时识别并重构代码异味对于提高软件质量至关重要。
本文选择其中一种面向对象的代码异味进行研究,过度耦合消息链的代码异味,研究其自动化检测方法。在面向对象应用程序中,这种异味的存在增加了Android应用程序的能耗,降低程序的可维护性[8]。
随着深度学习的快速发展,深度学习在自然语言处理、图像识别和生物信息等研究领域中崭露头角,软件工程领域的学者们也尝试将这种技术应用于代码异味检测领域,并取得了良好的效果[9] [10] [11]-[13]。因此,本文提出一种基于深度学习的过度耦合的消息链异味检测方法。
2. 相关工作
2.1. 面向对象代码异味
1999年,Martin Fowler为了阐明传统桌面应用程序中的设计问题,首次提出了“代码异味”这一概念[14]。代码异味并不是错误,但它们暗示了代码中可能存在的问题或需要改进的地方。这些异味增加了程序的维护难度,并妨碍了程序的演进。在其经典著作《重构:改善既有代码的设计》第二版中,Fowler列出了24种代码异味,例如过长的方法、过度耦合的消息链和重复代码等[10]。这些被后续研究称为面向对象(Object Oriented,简称OO)代码异味。面向对象代码异味通常源于开发者对某些原则或编程范式的过度追求或误用。例如,过度依赖设计模式、继承和泛型等手段可能会导致代码的可读性和可维护性下降。本文将研究MC异味,属于面向对象代码异味的范畴。
2.2. 过度耦合的消息链代码异味(MC)
过度耦合的消息链异味是面向对象代码中的一种问题,指的是实现一个类的功能时需要依次调用多层方法。在这种情况下,一个对象的方法调用会触发另一个对象的方法调用,从而形成一个连锁的“消息链”。这种MC异味的出现会导致代码可读性降低、耦合度增高以及维护变得更加困难。图1展示了一段包含MC异味的代码。在第10行,类A的getB()方法返回类B的实例,而在第16行,类B的getC()方法则返回类C的实例。在main()方法(第4行)中,通过a.getB().getC().getValue()的方式调用这些方法,从而形成了一个消息链。
Figure 1. An example of MC smell
图1. 一段含有MC异味的代码段
2.3. 基于深度学习的代码异味检测方法
近年来,深度学习的研究取得了显著进展,其应用范围已经超出了传统领域,如图像识别和自然语言处理。软件工程领域的研究人员也开始探索将这一技术应用于解决软件工程中的各种问题,包括软件测试、源代码表示、源代码质量分析、重构和代码异味检测等,并取得了良好的效果。Khomh等人采用贝叶斯信念网络模型进行代码异味的检测,而Abdou等人则使用支持向量机(SVM)模型进行相应的工作。Fontana等人运用16种传统机器学习模型(如SVM、决策树、朴素贝叶斯和随机森林等)来检测四种面向对象代码异味,并对不同算法在代码异味检测中的效果进行了比较。为了更好地捕捉复杂的逻辑关系并提升检测效果,研究者们在传统机器学习的基础上,引入了深度学习技术用于代码异味的检测。刘辉[12]等人利用卷积神经网络(CNN)来检测上帝类异味,并显著改善了检测效果。在此基础上,他们将卷积神经网络与长短时记忆网络(LSTM)结合,针对四种代码异味进行检测。这些研究在训练模型时,不仅使用了传统的代码度量,还结合了代码文本信息(如变量标识符) [15]。Sharma等人实施了三种不同的深度学习模型,即循环神经网络(RNN)、卷积神经网络(CNN)和自编码器(AE),检测了包括上帝类在内的四种面向对象代码异味。Sharma提出,在模型训练过程中,代码度量的使用可能会影响模型的学习效果,因为代码度量可能限制模型捕获其他源代码特征的能力。此外,即使采样相同的代码度量,由于阈值设置不同,检测结果也会有所差异,而这些结果还受到度量选择的主观性影响。因此,Sharma选择将程序文本信息作为模型输入,替代传统的度量信息。与刘辉的文本信息不同,Sharma将源代码转换为整数,以数字向量的方式表示程序文本信息,从而涵盖了程序的全部信息。本文借鉴了这一方法,以提取源代码文本中的数据特征。
3. 数据集构建
在深度学习领域,针对代码异味检测的研究目前缺乏公开可用的数据集。为了填补这一空白并确保数据集的高质量,本研究依据以下标准从GitHub上选取了100个Android移动应用:使用Java语言编写,拥有至少500颗星,项目文件大小不少于1000 KB,且在2023年1月1日之后有代码更新。GitHub作为全球领先的代码托管和项目管理平台,其上的星标数量可以反映项目的受欢迎程度和认可度,而项目的更新频率则可以作为衡量其活跃度和维护状态的指标。因此,这些标准有助于筛选出质量较高的项目。表1展示了部分选定应用的相关信息。
Table 1. Information on 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 |
在深度学习的传统应用领域中,如图像处理,语音识别等,有大量带有标签的数据集供研究者使用。而在代码异味检测领域,还未有类似资源供研究者使用[14]。为了克服这个困难,刘辉团队[15]和Sharma团队[16]首先使用已有代码异味检测工具检测源程序的异味,然后将输出结果作为后续传统机器学习模型的训练集。本文借鉴这种方法,提出基于静态程序分析的Android代码异味检测方法和自动样本生成方法,并实现工具ASSD (Android-specific Smell Detection)。我们使用ASSD检测MC异味并自动生成正、负样本集,作为后续深度学习模型的输入。
工具ASSD的输出结果是程序代码,而传统机器学习模型的输入是数值型的特征矩阵。因此本文借鉴Sharma等人[16]的研究,使用开源工具Tokenizer将源代码转换为数字向量形式,这种转换可以允许传统机器学习模型从整个程序文本中学习更广泛的特征,不再局限于度量信息。
本文方法的输入如公式(1)所示。
(1)
其中,
为转换后的数字向量。代码文本信息作为传统机器学习模型输入的特征数据集,数据集格式如图2所示。
Figure 2. The data format for code text information
图2. 代码文本信息数据格式
4. 深度学习算法
在本研究中,我们采用了三种不同的深度神经网络架构来识别代码异味,包括卷积神经网络(CNN)、循环神经网络(RNN)以及它们的融合模型CNN-LSTM。CNN和RNN是两种广泛使用的深度学习模型,它们在代码异味检测任务中非常流行。接下来,本文将详细阐述这三种模型的设计和架构。
4.1. 卷积神经网络模型
卷积神经网络(CNN)是深度学习领域中一个极具代表性的算法,它是一种前馈神经网络,其核心是利用卷积操作来处理数据。本文采纳的CNN模型结构详见图3。该模型是在刘辉团队和Sharma团队[16]先前研究的基础上进行了优化。模型由六个主要层构成:首先是嵌入层,它负责将输入的离散特征映射成密集的低维向量形式。接着是卷积层,它通过移动卷积核在数据上捕捉局部特征。紧随其后的最大池化层对这些特征进行降维处理,以筛选出最重要的信息。Dropout层通过随机忽略一些神经元的输出来减少模型的过拟合风险。Flatten层的作用是将多维数据转换为一维序列。最后,全连接层通过矩阵乘法和偏置加法操作,将这些一维特征转换为最终的预测输出。
Figure 3. Classifier based on CNN model
图3. 基于CNN的神经网络分类器
表2列出了改进的CNN模型中选定的各层参数。在嵌入层,参数设定反映了输入数据集包含2239个不同的字符,每个字符被编码为8维的向量,同时输入数据的最大长度设定为88。
Table 2. CNN model layer parameter values
表2. CNN模型层数参数值
参数 |
真值 |
嵌入层的参数 |
{2239, 8, 88} |
卷积层中的过滤器 |
{32, 64} |
卷积层中的内核大小 |
{2, 3} |
最大池化层的大小 |
{2} |
最大迭代次数 |
{50} |
4.2. 循环神经网络模型
环神经网络(RNN)是深度学习中用于分析序列数据的另一种典型算法。图4展示了用于识别代码异味的RNN模型的结构图。该模型是在Sharma团队[16]之前的研究基础上进行优化的。模型由四个主要层次构成:首先是嵌入层,它负责将输入的离散特征映射成连续的向量表示;其次是LSTM层,即长短期记忆网络,它专门用来处理时间序列数据,并且能够捕捉序列中的长期依赖关系;接着是Flatten层,它将LSTM层输出的多维数据转换为一维向量;最后是全连接层,该层负责将一维数据用于分类或回归任务。这种结构在处理语言相关任务时表现出色。
Figure 4. Classifier based on RNN model
图4. 基于RNN的神经网络分类器
表3所示为模型的超参数。嵌入层的参数与之前提到的CNN模型中的设置保持一致,而在LSTM层中,参数指的是每层LSTM隐藏单元的维度。
Table 3. RNN model layer parameters
表3. RNN模型层数参数
参数 |
真值 |
嵌入层的参数 |
{2239, 8, 88} |
LSTM单元 |
{128} |
全连接层参数 |
{64, 32, 1} |
最大迭代次数 |
{50} |
4.3. CNN-LSTM网络模型
CNN-LSTM是CNN和RNN模型相结合的模型。刘辉团队和Sharma团队的研究中并未使用该模型。本文为探讨两种经典模型结合后的检测效果,将两种模型相结合检测MC异味。图5显示了CNN-LSTM模型的体系结构。模型由7层组成。
Figure 5. Classifier based on CNN-LSTM model
图5. 基于CNN-LSTM的神经网络分类器
LSTM层的输出连接到dropout层。dropout层首先根据概率删除部分神经元,然后开始训练更新未被删除的神经元和权值的参数,防止过拟合。在这种情况下,我们将层的dropout率设置为0.5,这表明要移除的神经元节点是随机选择的,概率为0.5。Dropout层的输出就是Flatten层的输入。Flatten层的输出被送入一个激活函数为relu函数的密集连接网络,其输出数据量为800。这一层用于根据所调查的异味来预测实例是否属于异味。本文模型的损失函数为binary_crossentropy函数,其优化器为RMSProp,迭代次数(epoch)为50次。表4显示了模型每一层的超参数。
Table 4. CNN-LSTM model layer parameters
表4. CNN-LSTM模型层数参数
参数 |
真值 |
嵌入层的参数 |
{2239, 8, 88} |
卷积层中的过滤器 |
{32, 64} |
卷积层中的内核大小 |
{3} |
最大池化层的大小 |
{2} |
LSTM单元 |
{128} |
嵌入层的参数 |
{1, 32, 64} |
全连接层参数 |
{50} |
5. 实验验证
5.1. 实验设计
本研究提出了一种基于深度学习的检测过度耦合消息链的代码异味方法,其流程图参见图6。流程的第一步是构建数据集,我们从GitHub上收集了使用Java编写的Android应用,并根据特定标准进行筛选。通过使用ASSD方法,我们从这些应用中提取了正样本和负样本。由于深度学习模型需要数字形式的输入,而我们的样本是代码片段,因此需要通过Tokenizer将这些正负样本转换为数字向量。接下来,我们对这些数字向量进行预处理,以便将它们作为输入数据提供给本文采用的三种传统机器学习分类器。通过这些分类器,我们得到了代码异味的检测结果,并使用模型评价指标来评估这些分类器的性能表现。
Figure 6. Flow chart of detection method
图6. 检测方法流程图
5.2. 模型评价指标
在本章中,使用查准率(Precision)、查全率(Recall)和F1值(F1-score)来评估不同分类器的性能。这些评估指标可以通过以下公式计算得出:
查准率(Precision)表示分类器正确预测为正样本的样本数量(True Positives,简称TP)与分类器预测为正样本的
样本总数(True Positives + False Positives,简称TP + FP)之比,其计算公式为:
(2)
查全率(Recall)表示分类器正确预测为正样本的样本数量(True Positives,简称TP)与实际正样本的总数(True Positives + False Negatives,简称TP + FN)之比,其计算公式为:
(3)
F1值(F1-score)是综合考虑了查准率和查全率的度量指标。它是查准率和查全率的调和平均值,可以用于评估分类器的综合性能。F1值的计算公式为:
(4)
通过计算这些评估指标,可以客观地评估不同分类器在检测异味共存方面的有效性和区别。这些指标提供了分类器的准确性和召回率的衡量,以及二者的综合表现。
5.3. 实验结果分析
三种模型都可以检测出MC异味,并且三种神经网络模型的查准率、查全率和F1值都较高。其中,CNN的检测效果最好。由此可见,使用深度学习检测MC异味是可行的。表5所示为不同神经网络分类器在相同特征下的检测结果。
Table 5. Results of MC detection using different models
表5. 不同模型检测MC的结果
神经网络分类器 |
查准率 |
查全率 |
F1值 |
CNN |
0.8843 |
0.9284 |
0.8846 |
RNN |
0.8821 |
0.8747 |
0.8704 |
CNN-LSTM |
0.8801 |
0.8677 |
0.8738 |
如表5所展示的,CNN模型在检测性能上略胜一筹,超过了RNN和CNN-LSTM模型。在深度学习的常见应用中,由于RNN模型在处理文本数据方面的能力较强,它在自然语言处理(NLP)领域得到了广泛的应用。考虑到程序源代码和自然语言文本之间存在许多相似性,人们可能会预期RNN在检测代码异味方面的表现会优于CNN。然而,Sharma等人指出,程序源代码和自然语言文本之间也存在显著差异,这些差异可能使得在处理文本方面表现出色的方法在处理代码时效果不佳。他们的研究还发现,在面向对象代码异味检测中,RNN模型的检测效果会因不同的异味类型而异,并不总是优于CNN,这也解释了为什么在某些情况下CNN的检测结果可能优于RNN。
6. 结语
为了填补深度学习在面向对象代码异味检测方面的空缺,提出基于深度学习方法检测Android应用程序中的过度耦合消息链的代码异味。其次,针对代码异味研究领域没有大量可供研究者使用的带有标签的样本集问题,使用程序静态分析技术,提出了Android异味样本自动生成方法,并实现了工具ASSD。然后,分析大量源码,从中提取出存在MC异味的代码段,并将源代码转换成数字信息作为模型输入的特征集,接着,使用CNN、RNN、CNN-LSTM模型进行检测。实验结果表明,基于深度学习的检测方法可以检测Android应用程序中的MC异味,而且检测效果为CNN > RNN > LSTM。
代码异味对Android应用程序的性能造成极大影响,不仅增加了电池的损耗缩短其使用寿命还增加了应用程序发生故障的可能。本文仅对MC这种面向对象的代码异味进行检测。Reimann等人的Android异味列表中共包括30种味道,在后续的研究中我们将在本文方法的基础上,对Android 特有代码异味进行检测。目的是探索深度学习模型检测Android代码异味时,针对不同的异味,检测效果是否有差异。同时研究异味的重构方法,最终提高软件的质量。
NOTES
*通讯作者Email: 18845750183@163.com