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

1.1 项目GitHub地址

https://github.com/ReWr1te/WcPro

1.2 项目PSP表格

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

1.3 个人接口及其实现

本次项目基本任务部分共分为输入控制、核心处理、输出控制和其他(本组计划添加GUI)组成,我所负责的部分为输入控制。(备注:由于负责优化的同学参与了代码的后期编写,所以最终结果可能和我的基本功能有些许出入,最终实现以GitHub代码为准)

1.3.1 任务需求

任务需求为:对输入进行有效性校验,识别和处理无效输入,并针对有效输入,从中提取所需数据。

根据需求定义了如下接口:

外部接口负责和其他模块通信,调用内部子接口实现功能:

/**
* get the content in a file and return the result
*/
public String parseCommand(String[] args)

子接口分别完成参数处理、获取地址、获取文本内容以及对于文本内容是否为半角字符的判断:

/**
* judge the input parameters
*/
public int paraHandling(String[] args)
/**
* get the address
*/
public String getAddress(String fileName)
/**
* get the content
*/
public String getContent(FileReader fr, BufferedReader br, StringBuffer sb, String address)
/**
* check half-width characters
*/
public int halfWidthChar(String content)

下面取两个典型代码段分析(全部代码请参见GitHub源码):

1.3.2 参与外部调用的接口,主函数通过调用该接口来读取文件并获得文件内容:

/**
* get the content in a file and return the result
*/
public String parseCommand(String[] args) { // initiate content file, each line, filename and address to store related info
String address, content; // initiate file reader, buffered reader and string buffer to store file medially
FileReader fileReader = null;
BufferedReader bufferedReader = null;
StringBuffer stringBuffer = null; // call the functions to get address and then content
if (paraHandling(args) != 1) {
// to return a void result
return null;
} else {
address = getAddress(args[0]);
content = getContent(fileReader, bufferedReader, stringBuffer, address);
if (halfWidthChar(content) == 0) {
return null;
} else {
return content;
}
}
}

该接口结构简单清晰,因为代码已经很好地封装在了其它函数中。

在完成了必要变量的初始化之后,直接使用简单的if结构调用内部函数就能完成相应功能。

调用内部函数的顺序为:

参数判断→获取地址→获取文本→半角字符判断

1.3.3 半角判断的函数,通过对于字符串字节长度的比较来判断文本内容是否全为半角字符:

/**
* check half-width characters
*/
public int halfWidthChar(String content) {
if (content.getBytes().length == content.length()) {
return 1;
} else {
System.out.println("half-width characters only!");
return 0;
}
}

1.4 测试用例的设计以及测试效率的满足

1.4.1 本次测试用例的设计采用黑盒测试和白盒测试相结合的方式

黑盒测试通过不同的输入类型(包含输入参数对应的文本文件内容类型)来测试功能的正确性;白盒测试在尽量覆盖所有路径(函数结构比较简单,所有路径大致与下列测试用例相符)的情况下对每个路径进行参数与返回值的匹配正确性测试:

1.4.2 单元测试代码举例如下:

/**
* #0 zero-parameter test (black-box)
*/
@Test
public void parseCommandTest0() throws Exception {
String[] paras = {};
System.out.println("parseCommand() Test0 started.");
assertEquals("Failed",null, cp.parseCommand(paras));
System.out.println("parseCommand() Test0 succeeded.");
System.out.println("parseCommand() Test0 finished.");
}

所有测试用例请参见GitHub输入测试类。

1.5 测试用例的运行截图

1.5.1 单个测试用例的运行截图(代码参照上述代码):

1.5.2 所有用例的运行截图:

可以看出,每个测试用例都通过了,意味着实际输出与预期输出相一致,测试用例的质量和源代码质量都符合要求。

(所有测试用例见GitHub: UTCaseDesign_Input.xlsx,实现见测试类源代码)

1.6 小组贡献分

见毕博平台。

2 扩展功能:静态测试

2.1 选用的开发规范文档:《阿里巴巴Java开发手册》编程规约部分

该部分从命名风格、常量定义、代码格式、OOP规约、集合处理、并发处理、控制语句和注释规约八个方面详细地阐释了Java编程过程中应当注意和践行的规约,并且给出了恰当而形象的例子。

其中的编程规约主要分为“强制”、“推荐”和“参考”三类,以强制和推荐类型居多;强制的编程规约是要求程序员在Java开发过程中必须遵循的准则,如果不采用这些编程规范,则很有可能引发相当基础的错误,或者造成难以理解的歧义;推荐类型的规约则更像是告诉我们如何编写优雅高效的代码,通过简单的编程方式或者处理,我们可以让代码达到比较高的可读性和运行效果;参考给我们提供了更多可选择的编码方式,同时引发了更多关于编码技巧的思考。

举例理解:

《阿里巴巴Java开发手册》中指出:【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:if (condition) statements。 说明:即使是最简单的一行语句,在紧接着控制语句时也应当加上大括号,使得代码结构更加清晰完整。

根据我的实践体会举例如下:以前编写代码(特别是C和C++)为了方便,很多时候都会忽略控制语句后面接的单行语句,将它们写在控制语句的同一行上面,而且不加大括号,例如:

if (count == 0) return 1;

现在想来如果这行代码前后结构很复杂,在没有大括号的情况下确实容易和其他代码混淆。因此,在控制语句中必须使用大括号是一种很实用且规范的编码方式。

2.2 组员代码分析

分析代码的作者学号后5位:17121。

为了更好地阐述分析思路,下面先给出我所分析的这一段代码:

/**
* 输出
*/
public String output(List<Map.Entry<String,Integer>> list){
final String pathOfOutput="result.txt";
String outContent="";
Map.Entry<String,Integer> oMap;
int amount=0;//仅输出单词词频从高到低排序的前100个(从1到100)
for(Iterator<Map.Entry<String,Integer>> it=list.iterator();it.hasNext()&&amount<100;++amount){
oMap=it.next();
outContent+=oMap.getKey()+" "+oMap.getValue()+"\n";
}
outContent=outContent.substring(0,outContent.length()-1);//去除输出文件末尾多余的换行符
writeFile(pathOfOutput,outContent);
return outContent;
}

为了方便描述和理解,这里采用从前到后的顺序分析方式

  1. 第1~3行:注释,采用了/**内容*/的格式,符合规范(编程规约-注释规约-1-强制);
  2. 第4行:类方法定义,接口类中方法和属性不要加任何修饰符号(public也不要),需要改进;左大括号前加空格,需要改进(编程规约-命名风格-13-推荐,编程规约-代码格式-5-强制);
  3. 第5行:变量定义、赋值,名称:pathOfOutput,符合命名规范且语义明确;但双目运算符"="前后都需要添加一个空格,需要改进(编程规约-命名风格-4-强制,编程规约-代码格式-4-强制);
  4. 第6行:变量定义、赋值,名称:outContent,符合命名规范且语义明确;但双目运算符"="前后都需要添加一个空格,需要改进(编程规约-命名风格-4-强制,编程规约-代码格式-4-强制);
  5. 第7行:变量定义,名称:oMap,符合命名规范且语义较为明确(编程规约-命名风格-4-强制);
  6. 第8行:变量定义、赋值、注释,名称:amount,语义不够明确(例如改为wordAmount可能更好一些);双目运算符"="前后都需要添加一个空格,需要改进;方法内部单行注释应该在被注释语句上方另起一行,需要改进;注释的双斜线与注释内容之间有且仅有一个空格,需要改进(编程规约-命名风格-4-强制,编程规约-代码格式-4-强制,编程规约-注释规约-4-强制,编程规约-代码格式-6-强制);
  7. 第9~10行:for循环语句,for保留字与括号之间必须加空格,需要改进;双目运算符"&&"、"<"前后都需要添加一个空格,需要改进;左大括号前加空格,需要改进(编程规约-代码格式-3-强制,编程规约-代码格式-4-强制,编程规约-代码格式-5-强制);
  8. 第11行:赋值表达式,双目运算符"="前后都需要添加空格,需要改进(编程规约-代码格式-4-强制);
  9. 第12行:赋值表达式,双目运算符"+="和"+"前后都需要添加空格,需要改进(编程规约-代码格式-4-强制);
  10. 第13行:右大括号前后换行,符合规范(编程规约-代码格式-1-强制);
  11. 第14行:赋值表达式,双目运算符"="和"-"前后都需要添加空格,需要改进;定义或传递多个参数时,需要在逗号后加空格,需要改进;方法内部单行注释应该在被注释语句上方另起一行,需要改进;注释的双斜线与注释内容之间有且仅有一个空格,需要改进(编程规约-代码规范-4-强制,编程规约-代码规范-8-强制,编程规约-注释规约-4-强制,编程规约-代码格式-6-强制);
  12. 第15行:方法调用,定义或传递多个参数时,需要在逗号后加空格,需要改进(编程规约-代码规范-8-强制);
  13. 第16行:return语句,符合规范;
  14. 第17行:右大括号前换行,符合规范(编程规约-代码格式-1-强制)。

2.3 静态代码检查工具

此次选择的静态代码检查工具是阿里的 Alibaba Java Coding Guidelines,与之前使用的《阿里巴巴Java开发手册》高度吻合,因此比较统一。

我使用该工具的方法是下载IDEA插件,GitHub地址为:

https://github.com/alibaba/p3c

同时也可以在IDEA的如下界面——Preferences(MacOSX)配置(Windows在File-Settings里配置):

2.4 静态检查结果

截图如下,可见,自己编写的代码有很多不符合规范的地方,还需要慢慢学习和改正。具体不符合规范之处就不一一列举,感兴趣的读者可以参见《阿里巴巴Java开发手册》。


存在的问题以及改进方法:

  1. if/else后没有加大括号,就算是只有一行也应该加上;
  2. 出现魔法值,应该先定义常量;
  3. 没有添加类创建者信息,每一个类都应该添加创建者信息;
  4. 方法内部的单行注释都必须在原语句上另起一行;
  5. 类、类属性、类方法的注释必须符合形如/**注释*/的规范。

2.5 小组代码整体分析

通过之前的组员互评以及后续的讨论,我们发现我们的代码规范问题主要集中在注释和if/else等控制语句后的大括号上面,通过与《阿里巴巴Java开发手册》对比,我们将我们的代码进行了改进,并最终呈现为最后提交到GitHub上的代码。

我自己修改后的部分代码及检测结果如下:

可见所有代码都符合了对应的编程规范。

3 高级任务:性能测试和优化

3.1 测试数据集设计思路

  1. 单元测试数据集:单元测试数据及也应采用黑盒测试和白盒测试相结合的方式,完整覆盖每一个功能,如果采用路径覆盖的白盒测试,就应做到对所有路径的完全覆盖;
  2. 集成测试数据集:基于黑盒测试设计思想,对整个程序的功能进行测试,选用内容足够大且复杂的文件进行测试。

3.2 优化前的程序性能指标

由于各用户机器类型和性能有所差异,这里给出预期的优化率代替性能指标,预期优化率:50%

附性能检测部分代码:

//获取开始时间
long startTime = System.currentTimeMillis();
//获取结束时间
long endTime = System.currentTimeMillis();
//输出程序运行时间
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");

3.3 同行评审过程

  1. 参与者:17122 17121 17098 17103;
  2. 主持人&记录员:17122;
  3. 评审员:17122 17121 17098 17103(交叉互审);
  4. 讲解员:17098(代码深入分析、知识普及);
  5. 评审内容:各功能模块;
  6. 意见小结:代码规范可以改进、代码可以持续优化。

3.4 评审结论

影响性能的主要因素是循环的低效率和正则式自身的可优化特性,与同行评审大致相符。

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

3.5 优化设计思路

去掉不必要的循环,同时采用比正则式更优的分词方式,性能指标同比增长100%(耗时为50%)。

3.6 附加功能

我们小组设计实现了GUI设计。

3.7 项目小结

个人认为它们的大致联系为:

基本任务——软件开发;

扩展任务——软件测试;

高级任务——软件质量;

当然,不可否认的是,基本任务中也包含了单元测试,但我认为单元测试也算是开发工作的一部分,同时也肯定是整个测试过程中的一环;值得注意的是,测试工作其实贯穿从基本任务到高级功能实现的整个过程,从始至终为高质量的软件开发提供支持和保证。

软件开发是手段,软件测试是工具,软件质量是目的。在产品开发的整个过程中我们都需要兼顾三者,权衡比重,保证软件过程的顺利、高效进行。

WcPro项目(WordCount优化)的更多相关文章

  1. 仿百度壁纸客户端(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸客户端(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸客户端(一)--主框架搭建,自定义Tab + ViewPager + Fragment 仿百度 ...

  2. HUST软测1504班第4周小组作业成绩:WordCount优化

    说明 本次公布的成绩为第四周作业的结果: 第4周小组作业:WordCount优化 博客推荐:本次作业有一位同学完成有创意,推荐优秀博客.(优秀博客不会对成绩带来正面或者负面影响)PS:做任何创新的任务 ...

  3. 仿百度壁纸client(六)——完结篇之Gallery画廊实现壁纸预览已经项目细节优化

    仿百度壁纸client(六)--完结篇之Gallery画廊实现壁纸预览已经项目细节优化 百度壁纸系列 仿百度壁纸client(一)--主框架搭建,自己定义Tab + ViewPager + Fragm ...

  4. vue+webpack+element-ui项目打包优化速度与app.js、vendor.js打包后文件过大

    从开通博客到现在也没写什么东西,最近几天一直在研究vue+webpack+element-ui项目打包速度优化,想把这几天的成果记录下来,可能对前端牛人来说我这技术比较菜,但还是希望给有需要的朋友提供 ...

  5. 【Vuejs】335-(超全) Vue 项目性能优化实践指南

    点击上方"前端自习课"关注,学习起来~ 前言 Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 D ...

  6. 个人项目WordCount(C++/QT)

    个人项目WordCount(C++/QT) GitHub项目地址:https://github.com/Nova-cjp/Word-Count 百度云链接:https://pan.baidu.com/ ...

  7. vue大型项目高性能优化----想说爱你真的不容易

    一.背景   目前公司的电子合同采用表单设计器+合同业务配合实现,做了半年多后终于上线,但是下边员工普遍反映卡顿,甚至卡死,爆栈.尤其是新增和修改合同页面,因为这部分数据量大,逻辑复杂,很容易崩溃,所 ...

  8. 第四周小组作业:Wordcount优化

    1.小组github地址 https://github.com/muzhailong/wcPro 2.PSP表格 PSP2.1 PSP阶段 预计耗时(分钟) 实际耗时(分钟) Planning 计划 ...

  9. WordCount优化

    Github 地址:chaosrings/wcPro 1.PSP2.1表格 psp 2.1 psp阶段 预估耗时(分钟) 实际耗时(分钟) Planning 计划 10 10 Estimate 估计这 ...

随机推荐

  1. 尝试去读SQLMAP源码(一)

    本人python 小菜比 一枚.拜读业界典范~~ 阅读sqlmap 的版本是1.1.6,目前应该是最新版. sqlmap.py 脚本中 72~83 def modulePath(): "&q ...

  2. 使用PHP做分页查询(查询结果也显示为分页)

    1.先把数据库里所有的数据分页显示在页面,并在显示数据的表格上方加上查询表单.(加上条件,实现目标结果.) <!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...

  3. tcp协议的简单理解

    tpc协议属于传输层协议,本篇主要介绍下几个概念,以及握手和挥手的过程. 1.tcp的几个概念 位码:即tcp标志位,有6种提示 SYN:synchronus,表示建立联机. ACK:acknowle ...

  4. java -jar参数携带问题

    方式一 -DpropName=propValue的形式携带,要放在-jar参数前面,亲测,放在它后面好像取不到值 java -fileName=JOURNAL_TREENODE_DATA-201904 ...

  5. Struts2优缺点

    优点: (1)  实现了MVC模式,层次结构清晰,使程序员只需关注业务逻辑的实现. (2)  丰富的标签库,大大提高了开发的效率. (3) Struts2提供丰富的拦截器实现. (4) 通过配置文件, ...

  6. vue-i18n国际化在data中切换不起作用

    vue-i18n是一个针对于vue的国际化插件,使用非常简单,具体使用方式看我细细道来. 实现方式 1. 下载包 npm install vue-i18n 2. 配置 在main.js文件中加入如下配 ...

  7. 解决Eclipse启动时报Initializing Java Tooling异常信息

    1.启动Eclipse报错:An internal error occurred during: "Initializing Java Tooling".java.lang.Nul ...

  8. Python内置模块之subprocess

    import subprocess ret = subprocess.Popen('netstat -ano',shell=True,stdout=subprocess.PIPE,stderr=sub ...

  9. Java Spring Boot VS .NetCore (四)数据库操作 Spring Data JPA vs EFCore

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  10. 编码 ASCII, GBK, Unicode+utf-8

    0. 1.参考 网页编码就是那点事 阮一峰 字符编码笔记:ASCII,Unicode 和 UTF-8 2.总结 美国 ASCII 码 发音: /ˈæski/ :128个字符,只占用了一个字节的后面7位 ...