第一次个人编程作业
这个作业属于哪个课程 | 班级链接 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 论文查重算法实现+单元测试使用+学会程序性能分析+程序结构设计 |
本人Github | https://github.com/DevilWizard/3119005431 |
页面导航
程序运行截图
计算模块接口的设计与实现过程
整体流程图
算法核心示意图
本项目中利用SimHash和海明距离作为查重核心算法
包结构
类结构
示意图如下:
说明:
- Check类是整个程序的主类,也是程序的入口点
- Algorithm是用于抽象算法的接口,方便后续扩展
- MyAlgorithm是Algorithm的具体实现类,通过这个类可以自定义查重算法,用到了
Builder
的设计模式 - SimHashUtils是用于计算论文相似度的核心算法工具类,它需要依赖TextUtils才能完成计算过程
- TextUtils是读写文件的工具类,负责读取论文txt文件和写入查重结果
计算模块接口部分的性能改进
性能展示
可以看到,内存分配最大的是int[]和BigInteger,也就是说对论文的分词和计算论文间相似度是性能消耗最大的步骤,对应消耗最大的两个函数是HanLP.extractKeyword()
和SimHashUtils.getSimHash()
。若要改进性能,可以从这两个方面入手。比如可以选取更优的分词和查重算法来解决
计算模块部分单元测试展示
说明:本项目中测试模块全部使用junit: 4.13.2
项目中还有其它测试模块,但篇幅有限,这里就只展示文件读写模块的单元测试
文件读写模块的单元测试
单元测试代码
@Test
public void readTxt_Test() {
String testFilePath1 = "src/test/files/readTxt_TestFile.txt";
//设定错误路径用于测试
String testFilePath2 = "src/test/file/readTxt_TestFile.tx";
try {
assertEquals("test\n", TextUtils.readTxt(testFilePath1));
assertThrows(FileNotFoundException.class, () -> TextUtils.readTxt(testFilePath2));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void writeTxt_Test() {
String testFilePath = "src/test/files/writeTxt_TestFile.txt";
assertThrows(FileAlreadyExistsException.class, () -> TextUtils.writeTxt("", testFilePath));
}
测试的函数和测试思路
这个测试的是TextUtils
中的readTxt()
和writeTxt()
方法,检测它们能否正确读写。对于readTxt()方法,从读取内容是否正确
和待读取文件是否存
在两个角度去测试,预先写入一个含有预设文本的txt文件,然后再调用这个方法读取,看它们是否相等,相等则说明第一个角度测试通过;预先设置一个错误路径,然后调用readTxt(),若抛出异常,则第二个角度测试通过。若两个角度都通过,则readTxt()
测试通过,否则测试失败。writeTxt()方法也是类似的思路。
测试覆盖率
TextUtils
测试覆盖率如下
计算模块部分异常处理说明
本项目用到的异常全部是jdk的原生异常,有以下几个:
1.FileNotFoundException
为了避免因为在读取文件的过程中有可能发生用户输错文件路径的情况,利用FileNotFoundException
,程序就会告知用户输入的文件路径有误并提醒用户修改重试
测试样例
@Test
public void readTxt_ExceptionTest() {
//设定错误路径用于测试
String testFilePath = "src/test/readTxt_TestFile.txt";
assertThrows(FileNotFoundException.class, () -> TextUtils.readTxt(testFilePath))
}
测试结果
2.FileAlreadyExistsException
为了避免出现文件覆盖的情况,当输出路径已存在同名文件时,利用FileAlreadyExistsException
,程序就会停止写入,并告知用户更换输出路径
测试样例
@Test
public void writeTxt_ExceptionTest() {
String testFilePath = "src/test/files/writeTxt_TestFile.txt";
assertThrows(FileAlreadyExistsException.class, () -> TextUtils.writeTxt("", testFilePath));
}
测试结果
3.IllegalArgumentException
这个异常是用来防止程序编写者因为粗心等原因在函数调用时传入了错误的参数而导致程序崩溃的(比如在调用check函数来对论文查重时可能出现参数为null的情况),利用这个异常,能够在出现调用参数错误时便于程序编写者快速地找到错误所在
测试样例
@Test
public void check_ExceptionTest() {
String testPath = "src/test/files/check_Test.txt";
assertThrows(IllegalArgumentException.class, () -> algorithm.check(null, testPath));
assertThrows(IllegalArgumentException.class, () -> algorithm.check(testPath, null));
assertThrows(IllegalArgumentException.class, () -> algorithm.check(null, null));
}
测试结果
PSP表格
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
20 |
30 |
· Estimate |
· 估计这个任务需要多少时间 |
20 |
25 |
Development |
开发 |
100 |
120 |
· Analysis |
· 需求分析 (包括学习新技术) |
60 |
70 |
· Design Spec |
· 生成设计文档 |
60 |
65 |
· Design Review |
· 设计复审 |
56 |
60 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
20 |
30 |
· Design |
· 具体设计 |
20 |
25 |
· Coding |
· 具体编码 |
220 |
260 |
· Code Review |
· 代码复审 |
40 |
30 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
50 |
45 |
Reporting |
报告 |
30 |
30 |
· Test Repor |
· 测试报告 |
20 |
20 |
· Size Measurement |
· 计算工作量 |
20 |
20 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
15 |
21 |
|
· 合计 |
706 |
780 |