基本+扩展+高级
基本任务:代码编写+单元测试
1.Github地址
https://github.com/ChenSilence/wcPro
2.PSP表格
PSP2.1 | PSP阶段 | 预估耗时 (分钟) | 实际耗时 (分钟) |
Planning | 计划 | 20 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 10 |
Development | 开发 | 360 | 400 |
· Analysis | · 需求分析 (包括学习新技术) | 20 | 20 |
· Design Spec | · 生成设计文档 | 25 | 25 |
· Design Review | · 设计复审 (和同事审核设计文档) | 5 | 5 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
· Design | · 具体设计 | 30 | 40 |
· Coding | · 具体编码 | 120 | 150 |
· Code Review | · 代码复审 | 30 | 30 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 60 | 90 |
· Test Report | · 测试报告 | 15 | 30 |
· Size Measurement | · 计算工作量 | 20 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 25 | 30 |
合计 | 440 | 500 |
3.代码设计思路
3.1接口描述
根据小组分工,我所负责的是词频统计模块,需实现的供上层模块调用的接口为count(String sPath)方法。
该方法接受一个字符串类型的参数作为源文件路径,执行该方法实现统计该文件中的词频,并将结果输出到map表中。
3.2设计思路
在第二周的WordCount程序基础上,本次任务要求统计不同单词的词频。由于上次开发过程中,为了统计单词数行数等功能,我已经了解并初步学习到,使用分割器split()函数处理字符串,实现类似的统计功能十分简单。因此本周统计词频任务,我立刻想到的就是依然利用split()函数。
那么如何合理使用该函数才能满足需求呢?这是我所主要考虑的问题。
于是我对老师对需求的详细说明,对单词的规定,进行了深一步的解读。
通过我的分析,各项对单词的规定主要分为两大类,如下
3.2.1无短横线连接的单词处理
例子1 “(see Box 3-2).8885d_c01_016,带数字、常用字符和单词的情况,视为4个单词,即see, box, d, c”。这是老师所规定的。
在本情况下,提取出连续英文字母构成的串就是单词。
3.2.2有短横线连接的单词处理
例子2 content-based,视为1个单词。
该情况若采用上述处理方法,则会将其处理为content 和 based两个单词,所以必须要对上述方法进行改进。
我所采用的解决方法是提取时将短横线也予以保留,即英文字母和短横线共同构成单词,这样又会带来新的问题,如下:
如-night或者night-都会被视作单词,而需求中明确说明,这种形式需被统计为night。
解决方法:去掉单词头或尾的短横线- 。
到此,单词提取完毕。
统计数目的设计思路,主要是利用java中相应的特殊容器类的函数来方便处理。
我将提取出来的单词转小写并放入String类型的list容器中。
遍历list容器,将单词和对应的频度放入Map中保存, key值为单词,value为频度。具体方法如下:
若单词不存在于map,则加入map,并置value为1,;若单词已存在于map,则该单词的value+1.
综上,设计过程结束,可以满足需求。下面是具体实现。
3.3具体实现
在设计思路中我已非常详细的说明了设计过程,以及编码思路,因此不再赘述,相信将代码结合注释放上来已非常清楚。
提取单词的函数divide(String text)
public void divide(String text){//将文本分割成多个单词 String[] words = text.split("[^a-zA-Z-]");//保留下只含字母和-的连续字符 for(String word : words) { if(word.length() != 0 ) {//去除空串,可能由多个非字母造成 if(word.charAt(0)=='-') {//单词如-abcd,去掉前面的- word = word.substring(1); } if( word.charAt( word.length()-1) == '-' ) {//单词如abcd-,去掉最后的- word = word.substring(0, word.length()-1 ); } lists.add(word.toLowerCase());//添加进lists } } }
统计单词个数的函数doCount()
public void doCount() { //单词的词频统计 for (String nowWord : lists) { if(map.get(nowWord) != null){ //单词已存在则value+1 map.put(nowWord,map.get(nowWord) + 1); }else{ map.put(nowWord,1);//单词不存在则加入,同时置value=1 } } }
4测试设计过程
主要是对divide函数进行测试,因为doCount函数的测试用例必定会在divide函数的测试用例中体现。
采用白盒测试与黑盒测试相结合的方式。用两种方法设计测试用例时其实是有重叠部分,但是所采用的设计思想不同。
由于判定节点数很少,程序流程并不复杂,因而我主要采用的是黑盒测试的思想来设计测试用例,白盒测试的测试用例较少,如下:
4.1黑盒测试
我依据的原则是等价类划分,如下:
4.2白盒测试
待测函数中只有如下三个判定节点
if(word.length() != 0 )
if(word.charAt(0)=='-')
if( word.charAt( word.length()-1) == '-' )
我采用的是条件组合覆盖。如下:
5.测试运行和评价
5.1单元测试的运行截图
如图所示20个测试用例均通过!
5.2评价单元测试效果
因为函数本身并不复杂,所设计的20个测试用例覆盖了所有典型的情况,在测试用例的设计上我觉得质量不错了。
只是在写测试脚本的时候,虽然用的是参数化测试的方法,但是对于预期的输出仍然是需要人工统计。希望以后可以了解到简单的非人工的方法。
5.3评价被测模块的质量水平
如上所示,针对20个测试用例,divide(String text)函数,都能准确的分割出合法单词,运行正确,且运行时间很快(少量数据)。
所以可以判断该单元(函数)正确率符合要求,对于大量数据的处理时间尚待考证。
扩展任务:静态测试
1.开发规范说明
我选择的是《阿里巴巴Java开发手册终极版v1.3.0》
节选其中的部分规范如下:
1.1命名
1.2代码格式
我认为这其中tab缩进的问题是大家平时不注意的,并且最容易涉及到的,而不同的编译环境下对tab的处理又有所不同
因此我觉得对于规定tab用四个空格来代替,是非常有必要的,十分值得学习。
2.交叉代码评审
我选择评审的16637组员代码节选如下:
1 // 传入的两个参数应该是map的两个key 2 public int compare(String a, String b) { 3 if (base.get(a) > base.get(b)) { 4 return -1; 5 } else { 6 return 1; 7 } 8 }
2.1命名
方法名采用了小写字母开头的单词,符合规范。
但是传递的参数名使用了随意的a,b来命名,不能望文生义,属于不好的地方,应当改进。
2.2代码格式
左大括号不换行,右大括号后出现了else他没有换行,而右大括号号表示结束的换行了,符合规范。
(base.get......base.get(b)) 左右小括号与字符中没有空格,符合规范。
if (base...... 保留字if与左括号之间使用了空格,符合规范。
// 传入的参数....... //与注释之间有且仅有一个空格。符合规范
代码中仍然使用了tab缩进,不可取。
3.静态代码扫描
3.1我使用的是checkstyle工具,下载地址为
3.2扫描结果和运行截图
不幸的是,当我使用该工具扫描后,几乎每一行代码都出现了warning。
代码的高亮截图如下:
控制台输出的截图
3.3结果分析
3.3.1关于默认检查规范太严格的讨论
当看到自己的代码每一行都被打上了warning,这是让人非常厌恶的事情。因此我去阅读了有关checkstyle的说明,想弄明白难道自己的代码真的这么不合规范吗?
然后我了解到,其实并非如此。而是缘于下述条件。
Checkstyle默认提供的配置文件有两个:一是Sun Checks,对应的配置文件是sun_checks.xml文件,含义是Checkstyle configuration that checks the sun coding conventions;一般Checkstyle默认的设置就是sun_checks.xml配置文件。另一个是Sun Checks(Eclipse),对应的配置文件是sun_checks_eclipse.xml,含义是Slightly modified version of Sun Checks that better matches the default code formatter setting of Eclipse.
一般Sun Checks的检查项限定得较为严格,我们实际项目中并不要求那么完美的编程规范。
这才使得自己的代码全部都不合规范,因此日后如果有时间可以尝试自定义配置文件,这么严格的规范对实际项目来说太吹毛求疵了。
3.3.2 实际有意义的检查规范
抛开了其本身的严格要求,检查结果对我们来说还是很有用的, 例如:
由该截图我看到,if 表达式的写法,由于只有一行语句我就没有使用花括号,然而规范提到if结构必须使用花括号等等。
每行都被指出的,普遍的不规范的地方是缩进的处理,我并没有遵循一定的原则,而是“顺眼”就好,这也是需要改进的地方。
总的来说checkstyle指出了我的代码中存在的不规范的地方,帮助我学习了规范的编码方式,也使我改进了一些不好的习惯。
4.组内代码分析
整个小组的代码接口清晰,每个人实现的功能单一,代码简洁。
而大家在写代码过程中,有大部分都是调用的库函数,使得执行效率高。
而代码冗余度方面,观察后发现大家很少有代码冗余,因此我觉得就小组整个项目来说,质量还不错。
经过小组内代码评价后,共同将代码进行了改进,使得代码更加规范。
高级功能
1.测试数据集
设置了不同大小的文本文件,有52kb,92kb,544kb,845kb,1500kb
2.同行评审
大家一致认为文件越大,处理时间越长。
其次,我们认为对于同样的文件大小而言,单词数越多,处理时间越长。
3.性能分析
上图为测试结果,对于文件大小这一点来说,确实是正相关,但是程序执行时间与文件大小间不是正比关系。
4.性能优化
对于我所负责部分的代码,找出其中值得优化的部分如下:
// if (word.length() != 0) {// 去除空串,可能由多个非字母造成 // // if (word.charAt(0) == '-') {// 单词如-abcd,去掉前面的- // word = word.substring(1); // } // if(word.length()>0) { // if (word.charAt(word.length() - 1) == '-') {// 单词如abcd-,去掉最后的- // word = word.substring(0, word.length() - 1); // } // } // if(word.length() >0 ) { // lists.add(word.toLowerCase());// 添加进lists同时转小写字母 // }
原本我的代码对于前后含短横线单词进行两次substring()处理,两次操作分别去掉前和后的短横线,增加了运行时间,内存开销也相对大。
改进后,我事先标记前后需要截取的位置,将截取工作用一次substring( )处理,如下:
if (word.length() != 0) {// 去除空串,可能由多个非字母造成 int left=0,right=word.length(); int i=0,j=word.length()-1; while(word.charAt(i) == '-') {//left第一个非短横线的字母下标 left++; if(i == word.length()-1) { break; } i++; } while(word.charAt(j) == '-') {//right从后往前第一个非段横线的字母下标 right--; if(j == 0) { break; } j--; } if (left <= right) { word = word.substring(left, right);//截取前后短横线包围的部分 lists.add(word.toLowerCase());// 添加进lists同时转小写字母 } }
改进代码后,再次运行相同的测试集,程序运行时间得到减少。
5.作业小结
通过本次实验,更加深刻的体会到了软件开发和软件测试,软件质量之间是迭代交替的关系。即第一代产品(代码)开发完成以后,势必会存在一些漏洞和性能上的不足。
通过静态检查,同行评审,压力测试等可以发现代码的不足和缺陷。然后软件开发人员应该针对发现的问题,改进自己的代码,只有这样才能获得高质量的产品。
我的小组贡献率为0.3。学号17001.
Thanks for reading!
参考链接:
www.cnblogs.com/ningjing-zhiyuan/p/8654132.html 第四周小组作业说明
https://blog.csdn.net/maritimesun/article/details/7668718 代码规范工具checkstyle使用手册
https://blog.csdn.net/seven_3306/article/details/8069948 Junit参数化测试的方法