第四周作业WordCount优化

基本+扩展+高级


 

基本任务:代码编写+单元测试

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

· 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工具,下载地址为

https://sourceforge.net/projects/eclipse-cs/files/Eclipse%20Checkstyle%20Plug-in/8.7.0/net.sf.eclipsecs-updatesite_8.7.0.201801131309.zip/download

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参数化测试的方法

 

posted @ 2018-04-08 19:04  bokeyuan_chen  阅读(195)  评论(4编辑  收藏  举报