WordCount优化

项目地址

https://github.com/YYCZ/WCPro

 

项目说明

  本次项目是小组合作的项目,具体项目要求不详细描述,见http://www.cnblogs.com/ningjing-zhiyuan/p/8654132.html。我们完成了项目要求中的基本任务、扩展任务和高级任务。

 

PSP表格

PSP2.1

PSP阶段

预估耗时

(分钟)

实际耗时

(分钟)

Planning

计划

 20

15

· Estimate

· 估计这个任务需要多少时间

 20

 15

Development

开发

 620

795

· Analysis

· 需求分析 (包括学习新技术)

 30

 90

· Design Spec

· 生成设计文档

 45

 60

· Design Review

· 设计复审 (和同事审核设计文档)

 15

 20

· Coding Standard

· 代码规范 (为目前的开发制定合适的规范)

 20

 10

· Design

· 具体设计

 60

 45

· Coding

· 具体编码

 360

 420

· Code Review

· 代码复审

 30

 60

· Test

· 测试(自我测试,修改代码,提交修改)

 60

 90

Reporting

报告

 100

 130

· Test Report

· 测试报告

 60

 90

· Size Measurement

· 计算工作量

 10

 10

· Postmortem & Process Improvement Plan

· 事后总结, 并提出过程改进计划

 30

 30

 

合计

 740

 940

 

 

基础任务

接口实现

  在这次WordCount中我负责的是输出控制模块:对结果以合理方式输出,将单词词频排序的结果输出到文件。 这次的小组代码里,我们首先通过Input模块获得输入文件文本,将得到的输入文件文本交给Core模块进行处理得到一个Word模块对象的ArrayList,最后把这个ArrayList交给输出模块以合理的方式输出。

  下面是我负责的Output模块的代码,我将通过对贴出的代码进行注释,对代码实现做出解释——

 1 package yycz;
 2 
 3 import java.util.ArrayList;
 4 import java.io.*;
 5 import java.io.IOException;
 6 
 7 public class Output {
 8 
 9     private String filePath;
10     private ArrayList<Word> words;
11 
12     public Output(ArrayList<Word> words) {
13         this.filePath="result.txt";//输出结果指定路径
14         this.words=words;//获得的要进行处理的ArrayList
15     }
16 
17     public void write() throws IOException {
18         String str="";
19         int flag =1;//使用flag标识是否是第一个列表对象
20         Word word;
21         for(int i=0;i<100&&i<words.size();i++){//当列表对象超过100个时,输出前100个;当不超过100个时,全部输出
22             word=words.get(i);
23             if(flag==1){
24                 str+=word.getStr()+' '+word.getFrequency();
25                 flag =0;
26             }
27             else str+="\r\n"+word.getStr()+' '+word.getFrequency();
28             //这样输出是为了实现“输出规定”中“单词和词频间空一格,输出文件末尾多余的换行符应去除“的要求
29         }
30         System.out.println(str);
31         File afile =new File(filePath);
32         try {
33             afile.createNewFile();
34         } catch (IOException e) {
35             e.printStackTrace();
36         }
37         BufferedWriter output = null;
38         try {
39             output = new BufferedWriter(new FileWriter(afile));
40         } catch (IOException e) {
41             e.printStackTrace();
42         }
43         output.write(str);
44         output.flush();
45         output.close();
46     }
47 }

 

测试用例设计

  由于对获取的对象进行排序的工作在Core模块中进行,Output模块仅仅进行“仅输出单词词频从高到低排序的前100个(从1到100)”和“控制输出格式”两个功能的实现,所以对这个模块测试用例的设计比较简单,只设计了三个测试用例(分为输出单词超过100个和不足100个),它们分别为:

{"hola:3 hello:1","1.txt"};

{"seas:22 would:19 rise:15 when:13 I:10 gave:7 the:5 word:1","2.txt"};

{"alone:200 arrow:199 apple:198 at:197 alive:196 all:195 angry:194 any:193 abuse:192 ability:191 ache:190 acheive:189 acomplishment:188 avoide:187 annouce:186 ah:185 ambulance:184 asia:183 asian:182 Africa:181"
+"Australia:180 Australian:179 attach:178 attachment:177 able:176 about:175 above:174 abroad:173 aceident:172 across:171 active:170 activity:169 ad:168 address:167 advertisement:166 afford:165 afraid:164 after:163 aai:162 ahe:161"
+"ago:160 agree:159 aha:158 air:157 alike:156 all:155 allow:154 almost:153 alone:152 alreaddd:151 also:150 am:149 always:148 amaze:147 baby:146 back:145 backward:144 bacteria:143 bad:142 badly:141"
+"bag:140 bah:139 bake:138 ball:137 balance:136 balloon:135 bang:134 band:133 banana:132 bank:131 bare:129 banner:128 bargain:127 bark:126 barn:125 barrel:124 base:123 basic:122 basically:121"
+"basin:120 basis:119 basket:118 bat:117 bath:116 bathe:115 bathroom:114 bay:113 battle:112 beach:111 beam:110 bean:109 bear:108 beard:107 beast:106 beat:105 beauty:104 because:103 bed:102 bee:101 beef:100 beer:99 before:98 beg:97 behalf:96 behave:95 being:94 bell:93","much.txt"}

  另外由于此模块能够设计的测试用例较少,我还参与了其他成员模块的测试用例设计,主要对单词判别的部分进行。例如:测试输入为空的边界情况,测试大小写字母及其混合,测试大小写单词及其混合,测试单词词频统计,测试对-的处理,测试~作为分隔符,测试!作为分隔符,测试_作为分隔符,测试数字作为分隔符,以及测试非英文字母(如日文),测试奇怪的符号,尽可能覆盖单词判别的所有情况。

 

单元测试截图

 

  从图中可以看出测试质量良好,测试时真实的发现了当“测试非英文字母”时的bug,而被测模块的质量也可以从上图中的通过情况看出。

 

小组贡献分           

  根据小组讨论的结果,我的小组贡献率为0.24。(注:此为基本、扩展及高级任务的总体贡献率

 

扩展任务

关于开发规范

 

  我阅读的开发规范是《阿里巴巴JAVA开发手册》,《阿里巴巴Java开发手册》中指出:【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。 说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。反例:

String str = "start"; 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。  反例:String key = "Id#taobao_" + tradeId;       cache.put(key, value);    
for (int i = 0; i < 100; i++){         
     str = str + "hello";      
}    

  根据我的实际体会举例如下:一开始我进行输出处理时使用的就是简单的“+”使得字符串拼接,在读完规范后我尝试使用append()方法,虽然内存资源的情况我并不知道有没有改善,但好歹学到了东西。

  我使用这个开发规范分析了编写Core模块的17178小组成员的代码(具体代码见Github),其代码的未遵循的规范体现在:

  • 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。  

   反例:String key = "Id#taobao_" + tradeId;      

      cache.put(key, value);

  • 【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。 

  但同时其代码也遵循了许多良好的规范

 

  • 【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
  • 【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式 也要避免采用。

   正例:alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。 

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

   正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

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

   正例: localValue / getHttpMessage() / inputUserId

 

 

  • 【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。 说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。      
  • 【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成: 
if (condition){ 
  ... 
  return obj; 
}

// 接着写 else 的业务逻辑代码;
  • 【推荐】除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复 杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

   说明:很多 if 语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么 样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢? 

正例: 
// 伪代码如下 
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {    
  ... 
}   

反例: 
if ((file.open(fileName, "w") != null) && (...) || (...)) {    
  ... 
} 

  在使用代码规范检查别人的代码的同时,也加深了自己对于开发规范的理解,受益匪浅。

 

关于静态代码检查及发现的问题

   我们小组的静态代码检查工具选择的是Checkstyle,下载地址为:http://checkstyle.sourceforge.net/。该工具对我负责的Output模块的扫描结果如下图所示:

  其中代码存在的问题有:

  1. 不应使用 '.*' 形式的导入 - java.io.* 。
  2. 导入语句'java.io.*' 字典顺序错误。应在'java.util.ArrayList'之前。
  3. 导入语句'java.io.IOException' 字典顺序错误。应在'java.util.ArrayList'之前。
  4. line 5 Checkstyle Problem
    第 4 个字符 '}'应该与当前多代码块的下一部分 (if/else-if/else, do/while 或 try/catch/finally)位于同一行。

  5. line 24 Checkstyle Problem
    缺少 Javadoc 。

  6. WhitespaceAround: 'if' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  7. WhitespaceAround: 'for' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  8. WhitespaceAround: '==' is not preceded with whitespace.
  9. WhitespaceAround: '==' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  10. WhitespaceAround: '=' is not preceded with whitespace.
  11. WhitespaceAround: '=' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  12. WhitespaceAround: '+=' is not preceded with whitespace.
  13. WhitespaceAround: '+=' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  14. WhitespaceAround: '+' is not preceded with whitespace.
  15. WhitespaceAround: '+' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  16. WhitespaceAround: '{' is not preceded with whitespace.
  17. WhitespaceAround: '-' is not preceded with whitespace.
  18. WhitespaceAround: '-' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
  19. Package name 'WCPro.yycz' must match pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$'.
  20. GenericWhitespace '>' should followed by whitespace.

  可以看出,Output模块中存在很多格式问题以及导入语句问题。我也根据提示对代码的非格式问题进行了相应的修改。

  整个小组的代码主要存在的问题就是字符缩进不统一,代码改进的方法:制定统一的代码编写规范。

 

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

  因为程序的逻辑设计比较清晰,所以我们直接从测试数据集的量上面入手,所以思路就是用足够大的文件。这里我们直接采用英文的《飘》txt书籍进行数据集的设计。

  在之前我们使用的是朴素的词频统计方法,对《飘》进行词频统计的结果如下:

  

  之后进行了小组讨论,由17172主持,17178,17172,17156参与评审。17172指出程序中最耗时的部分毫无疑问是核心处理模块。17178分享了编写核心处理模块时的思路。17176赞成用哈希表完成词频统计会是更优的方案。17156探讨了用计数排序进一步提升排序速度的可行性。讨论结束我们一致认为使用哈希表将大大提高程序的效率。

  使用哈希表后,结果如下:

  

  可以看到在使用哈希表后,程序的执行速度有了显著的提升。这是因为词频统计算法的复杂度从O(n^2)降到了O(n)的缘故。

  通过本此作业实践,我体会到软件开发需要与软件测试同时进行,没有软件开发就没有测试,软件开发提供软件测试的对象。软件开发和软件测试都是软件生命周期中的重要组成部分,都是软件过程中的重要活动。而软件测试是保证软件开发产物质量的重要手段。通过测试,我们可以尽早发现软件缺陷,并确保其得以修复;最后,完善的测试为软件可靠性与安全性评估提供了重要依据。

 

posted @ 2018-04-08 12:50  Lililikeu  阅读(455)  评论(0编辑  收藏  举报