第一次个人编程作业
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 60 | 90 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 120 | 180 |
· Design Spec | · 生成设计文档 | 20 | 10 |
· Design Review | · 设计复审 | 10 | 10 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
· Design | · 具体设计 | 30 | 40 |
· Coding | · 具体编码 | 720 | 800 |
· Code Review | · 代码复审 | 200 | 400 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 150 |
Reporting | 报告 | 120 | 150 |
· Test Repor | · 测试报告 | 20 | 20 |
· Size Measurement | · 计算工作量 | 10 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
· 合计 | 1445 | 1815 |
二、计算模块接口
-
计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。
答:本次编程作业代码主要分为六个类:主调度类、检测类、读文件类、写文件类、字型判断工具类、拼音转换工具类。- 读文件类解析好的敏感词集合以及待检测文本列表后,交由检测类进行检测处理。接着检测类在字型判断类和拼音转化类的配合下,过滤敏感词,并将检测结果按输出格式存入列表中。之后将结果列表交给写文件类,完成输出任务。
- 算法关键:关键方法MatchSensitiveWordMap()生效的基础是DFA算法以及根据其构建出的DFA敏感词搜索树。但我当时了解到的DFA算法只能做到无变体敏感词的检测,无法识别经过拼音、英文大小写替换、插入无意义字符等的功能。因此,我在UpdateSensitiveWordMap()方法中构建DFA搜索树时,还额外构建了拼音-汉字的映射集合以及英文字母大写-小写的映射集合。接着在MatchSensitiveWordMap()中原DFA算法里添加了多个附加判断(多重的条件判断),并在每轮检索过程中累加形成原敏感词字符串(如“傻子”)以及变体敏感词字符串(如:“傻&……&zi”)。
-
计算模块接口部分的性能改进。记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2019、JProfiler或者Jetbrains系列IDE自带的Profiler的性能分析工具自动生成),并展示你程序中消耗最大的函数。
答:下面先放一张用JProfiler工具生成的性能分析图。
- 说实在话,我万万没想到是第三方库的方法消耗最大。不过抛开第三方库函数不说,我自己写的程序中,MatchSensitiveWordMap()方法不出意料地登顶消耗第一的榜首。原因在于这个方法是在基础DFA算法上拓展起来的,但是由于一开始设计不周全,导致写出来的代码耦合度很高,没法将各个检测模块分离开来。不仅代码冗余度较大,而且存在多重判断,代码繁杂,debug困难。
- 改进的思路就是直接将不同模式的过滤直接分成不同类的功能模块,在一个调度类里写一个循环,然后在循环体里必要的地方设置不同模式的敏感词检测。(目前暂时没有时间重构代码,接下来将会考虑这方面,同时补全剩余未实现的检测功能)
-
计算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。
答:项目部分单元测试代码(代码如下,该单元测试直接测试核心的敏感词匹配功能)
@Test
public void test1(){
FileReader fileReader =new FileReader();
ArrayList<String> ans = fileReader.readCheckedFile("D:\\javatest\\answer.txt");
BasicSensitiveWordChecker basicSensitiveWordChecker = new
BasicSensitiveWordChecker(fileReader.readCheckedFile("D:\\javatest\\a.txt"),fileReader.readSensitiveWordFile("D:\\javatest\\b.txt"));
ArrayList<String> testResult = basicSensitiveWordChecker.FindSensitiveWord(0);
Assert.assertEquals(ans,testResult);
}
}
下面是单元测试得到的测试覆盖率截图
关于测试函数的说明
- 在answer.txt文件中存有事先准备好的正确检索结果,通过读文件类方法readCheckedFile()读入列表中
- 接着分别调用读文件类的readCheckedFile()方法和readSensitiveWordFile()方法分别将a.txt中的待测文本以及b.txt中的敏感词库读入,并初始化检测类
- 调用检测类的检测方法得到一个结果列表,将该列表与最开始得到的正确结果列表相比较,判断是否相同,相同则通过测试。
测试文件answer.txt、a.txt、b.txt的截图如下所示
构造测试数据的思路
首先我的代码还未实现部首偏旁拆分检测以及拼音缩写替换检测。因此我就主要针对剩下几种已经实现的过滤功能设计测试数据。尽量让一个敏感词变体具有多种变体形式混杂的特点,这样才能达到真正的代码测试效果。
-
计算模块部分异常处理说明。在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。
答: 本代码内主要处理的异常为io异常-
读文件类异常处理
该异常主要是防止读文件类访问不存在的文件路径。
如下图所示,D:\javatest\d.txt的文件路径不存在,程序就会报错,报告找不到文件
-
写文件类异常处理
该异常主要是防止写文件类访问不存在的文件路径。
如下图所示,F:\javatest\d.txt的文件路径不存在,程序就会报错,报告找不到文件
-
三、心得
-
不要磨洋工
- 这次作业本来应该有充足的时间来设计和实现功能的,但由于我对作业的难度预期不足,以及我自身的懒散心态,导致作业完成进度一拖再拖,这也导致了后面几天真变成了老师所说的ddl驱动了。没必要,真的没必要。
-
设计与日常并非泾渭分明
- 回想从前,已经不止一次写过编程大作业了。在这一过程中,我不知为何,自然而然有了这样的感触:开发一样产品并非一日之功,对于产品设计的思考,应该能和日常生活交织在一起。细想起来,确实应该如此。毕竟,有的灵感,是不可能在一两个小时的枯坐中产生的。睡觉前想,洗澡时想,吃饭时想,或许新颖有趣的想法就能应运而生。
-
增强使用搜索引擎的能力和学习积极性
- 在这次的开发过程中,我很惊奇的发现,有些人的学习能力非常强,他们能从网上快速筛查出自己想要的知识,并迅速消化同时付诸实践。原因无非有二:首先他们能熟练使用搜索引擎获取到他们想了解的东西,其次是他们对新知识的渴望和热情,促使他们很快理解知识并很好地运用于实践中。
-
多多与人讨论交流
- 刚刚也说了,身边有很多学习能力超强的“巨佬”,我的学习能力没那么强,那我就“抱大腿”啊!在他们的交流中,总能发现一些常人发现不到的新颖的东西,这也是我们不断提升自己所必须具备的,没法像他们那样像团火一样发光,那么至少靠近一点让自己不那么暗淡,再进一步,或许还能点燃自己呢。