WordCount优化

WordCount优化



基本任务


项目地址:https://github.com/ReWr1te/WcPro

PSP表格

PSP2.1 PSP阶段 预估耗时 实际耗时
(分钟) (分钟)
Planning 计划 20 20
· Estimate · 估计这个任务需要多少时间 60 65
Development 开发 90 102
· Analysis · 需求分析 (包括学习新技术) 10 5
· Design Spec · 生成设计文档 10 12
· Design Review · 设计复审 (和同事审核设计文档) 30 35
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 20 25
· Design · 具体设计 10 5
· Coding · 具体编码 80 90
· Code Review · 代码复审 20 25
· Test · 测试(自我测试,修改代码,提交修改) 30 25
Reporting 报告 120 120
· Test Report · 测试报告 60 35
· Size Measurement · 计算工作量 20 25
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 20
合计 600 609

接口实现思路

按照小组讨论的分工,我负责的是从字符串中提取并且统计词频,接口为:

HashMap<String, Integer> parseContent(String str)

这个接口接受一个String参数,表示要分析的字符串内容,返回HashMap泛型部分表示单词以及对应的词频。

下面来看一下对于单词的定义,单词规定如下:

满足如下两个条件中的任意一个条件,则视为单词,

第一,由连续的若干个英文字母组成的字符串,例如,software,

第二,用连字符(即短横线)所连接的若干个英文单词也视为1个单词,例如,content-based,视为1个单词。

注意,单词不区分大小写,不考虑英文以外的其他语言,且仅考虑半角。

有关单词识别的部分典型情况的说明:

第一,Let’s,这种包含单引号的情况,视为2个单词,即let和s。

第二,night-,带短横线的单词,视为1个单词,即night。

第三,“I,带双引号的单词,视为1个单词,即i。

第四,TABLE1-2,带数字的单词,视为1个单词,即table。

第五,(see Box 3–2).8885d_c01_016,带数字、常用字符和单词的情况,视为4个单词,即see, box, d, c。

其实这么长一串总结起来就两句话:

  • 单词是类似于 a、a-a、a-a-a 这种。
  • 单词字母要转换成小写。

因此可以使用正则表达式来实现,代码如下(相关注释在代码中给出):

public class ContentParser {
    private String regEx = "[a-zA-Z]+(-[a-zA-Z]+)*";
    private Pattern pattern = Pattern.compile(regEx);
    //输入文件内容,返回单词统计信息
    @Test
    public HashMap<String, Integer> parseContent(String content) {
        HashMap<String, Integer> wordMap = new HashMap<>();
        if (content == null || "".equals(content)) {
            return wordMap;
        }
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            String word = matcher.group().toLowerCase();
            if (wordMap.containsKey(word)) {
                wordMap.replace(word, wordMap.get(word) + 1);
            } else {
                wordMap.put(word, 1);
            }
        }

        return wordMap;
    }
}

用例设计

这里采用白盒测试黑盒测试的思路来进行用例设计(由于用例数量较多,这里仅给出 链接),下面分别进行说明:

黑盒测试采取等价类划分的方法,通过对上面单词的定义进行分析,对输入有如下四条划分规则:

  • 是否含有字母
  • 是否含有数字
  • 是否含有特殊字符(除-,因为-可以被包含在单词内)
  • 是否含有-

据此可以产生16个测试用例。

白盒测试对函数的路径和判定进行分析,可得到如下程序图:

通过对代码循环部分分析可以合并两个测试用例,得到五个测试用例。

测试用例文档如下:

通过上面的分析可以得出结论,黑盒测试能够包含所有输入的等价类,白盒测试路径覆盖率为100%,测试用例足够少,测试足够完善,因此能够满足测试效率要求

测试结果

在代码实现时,将测试用例按照一定格式提前写入txt文件,测试时动态读取,方便了测试用例的动态管理,测试结果如下:

测试用例采用黑盒测试和白盒测试两种方式进行设计,黑盒测试能够包含所有输入的等价类,白盒测试路径覆盖率为100%。但是两者中出现了一部分重复的测试用例,说明黑盒测试和白盒测试具有一定的共同点。

被测试的程序通过了所有的测试用例,说明被测程序能够很好的完成程序功能,正确应对各种合理输入数据,并且能够正确处理异常输入数据,健壮性较好。


静态测试


这里根据《阿里巴巴Java开发手册》进行代码规范分析。《阿里巴巴Java开发手册》中指出,

【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。

据此我将正则表达式的预编译放在了对象初始化的过程中,这样方法不论被调用多少次,预编译只会执行一次,提高了正则匹配速度和方法执行速度:

public class ContentParser {
    private String regEx = "[a-zA-Z]+(-[a-zA-Z]+)*";
    private Pattern pattern = Pattern.compile(regEx);
    //输入文件内容,返回单词统计信息
    @Test
    public HashMap<String, Integer> parseContent(String content) {
        ······
        return wordMap;
    }
}

另外,《阿里巴巴Java开发手册》中指出:

【强制】类名使用UpperCamelCase风格,但以下情形例外:DO/ BO / DTO/ VO/ AO/ PO等。

【强制】方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式

这两点对类和方法、参数等的命名做出了规范,类统一使用大驼峰命名法,方法、变量、参数等统一使用小驼峰命名法。这样可以使得代码阅读更加方便。因此在程序中类命名为ContentParser 变量方法命名类似于wordMapparseContent

代码规范分析

这里分析了学号为17121的同学的代码。

从变量和方法等的命名来看 ,变量和方法的命名采用了小驼峰命名法,类的命名采用了大驼峰命名法,如sortwriteFile() 等,符合《阿里巴巴Java开发手册》中对命名规范的规定。

从工程目录规范的角度来看 ,《阿里巴巴Java开发手册》中指出:

【强制】单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。

说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。

该同学的Test文件全部放在了src/test/目录下,符合开发手册的规定,方便了测试用例的管理,提高了源码构建的效率。

静态检查工具

我采用的静态检查工具是Alibaba Java Coding Guidelines,下载地址

检查结果

采用Alibaba Java Coding Guidelines对ContentParser进行检查,结果如下:

对ContentParserTest进行检查,结果如下:

下面对检查中出现的问题逐条进行分析:

  • 对应第一条,在ContentParser中添加了author信息

  • 对应第二条,因为ContentParser不是Test类,不需要以Test结尾因此属于误判。

  • 对应第三条,注释内容是对方法功能的说明,因此改为使用/**内容*/形式的注释。

  • 对应第四条,由于最多分析100个单词,因此将hashmap的初值定位130。

比较好的地方是,在ContentParserTest的finally语句块中,会检查文件输出流的状态,确保不论是发生异常还是正常退出都能及时关闭。

小组代码存在的问题

  • 没有及时关闭文件流
  • 代码中多处出现魔法值
  • 注释不规范,包括不能有尾行注释,添加author信息,使用javadoc规范注释等等

改正方法:

  • finally中判断文件流是否关闭否则关闭文件流

  • 使用final定义常量

  • 按照规范改写注释


高级任务


测试用例设计

我们选取了一本O'Reilly的JQury教程构造我们的测试用例,将其复制多次,得到了24.03M的txt文件,并进行测试。初步估计运行时间在1000ms以上。

优化前性能指标

测试结果如下:

次数 时间(毫秒)
1 1777
2 1994
3 1978
4 2023
平均 1943

同行评审及结论

  1. 角色分工:
    • 主持人&记录员:田诗园
    • 讲解员:邱利光 沃锦文 王启萌 田诗园
    • 评审员:邱利光 沃锦文 王启萌 田诗园
    • 作者:邱利光 沃锦文 王启萌 田诗园
  2. 评审结果及结论
    • 正则表达式效率较慢。
    • 考虑到程序对查找的性能要求比较高,应当选取合适的容器类。
    • 提高IO速度。

优化设计思路

  • 可以用状态机来提取单词,提高效率。
  • 单词统计我们采用HashMap来存储。当数据容量较少时其内部实现为一个链表,当数据量较大时,自动改用二叉树进行存储,有效提高了查询效率。
  • 输出单词时,改变原来的每个单词打开一次文件的做法,只打开一次文件,全部输出后关闭文件流,提高了IO速度。

作业感想

想一想这次作业,编码的时间大概只占到了三分之一左右,剩下的时间大约三分之一用在了设计用例,编写测试框架,剩下的三分之一时间用在了写博客上。

这门课叫软件质量测试,从这次作业也可以感受到,软件测试的过程是贯穿整个开发过程中的,不管是基本功能,还是扩展任务,再到高级任务,测试的思想一直贯穿其中,基本任务要根据自己负责的模块设计二十个测试用例,扩展任务则是对自己的代码进行静态检查,也是一种软件质量测试,高级任务则是进行压力测试,检查程序的健壮性。

通过一系列测试过程,一方面让我从另一个角度了解了自己写的程序,另一方面让我对程序的健壮性有了信心,毕竟一个简单的模块写了二十个测试用例Orz

我的理解就是软件测试 贯穿了软件开发 的整个过程,保证了所开发软件的质量

posted @ 2018-04-08 23:31  王启萌  阅读(197)  评论(2编辑  收藏  举报