项目地址
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模块的扫描结果如下图所示:
其中代码存在的问题有:
- 不应使用 '.*' 形式的导入 - java.io.* 。
- 导入语句'java.io.*' 字典顺序错误。应在'java.util.ArrayList'之前。
- 导入语句'java.io.IOException' 字典顺序错误。应在'java.util.ArrayList'之前。
-
line 5 Checkstyle Problem
第 4 个字符 '}'应该与当前多代码块的下一部分 (if/else-if/else, do/while 或 try/catch/finally)位于同一行。 -
line 24 Checkstyle Problem
缺少 Javadoc 。 - 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)
- 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)
- WhitespaceAround: '==' is not preceded with whitespace.
- WhitespaceAround: '==' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
- WhitespaceAround: '=' is not preceded with whitespace.
- WhitespaceAround: '=' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
- WhitespaceAround: '+=' is not preceded with whitespace.
- WhitespaceAround: '+=' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
- WhitespaceAround: '+' is not preceded with whitespace.
- WhitespaceAround: '+' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
- WhitespaceAround: '{' is not preceded with whitespace.
- WhitespaceAround: '-' is not preceded with whitespace.
- WhitespaceAround: '-' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3)
- Package name 'WCPro.yycz' must match pattern '^[a-z]+(\.[a-z][a-z0-9]*)*$'.
- GenericWhitespace '>' should followed by whitespace.
可以看出,Output模块中存在很多格式问题以及导入语句问题。我也根据提示对代码的非格式问题进行了相应的修改。
整个小组的代码主要存在的问题就是字符缩进不统一,代码改进的方法:制定统一的代码编写规范。
高级任务:性能测试和优化
因为程序的逻辑设计比较清晰,所以我们直接从测试数据集的量上面入手,所以思路就是用足够大的文件。这里我们直接采用英文的《飘》txt书籍进行数据集的设计。
在之前我们使用的是朴素的词频统计方法,对《飘》进行词频统计的结果如下:
之后进行了小组讨论,由17172主持,17178,17172,17156参与评审。17172指出程序中最耗时的部分毫无疑问是核心处理模块。17178分享了编写核心处理模块时的思路。17176赞成用哈希表完成词频统计会是更优的方案。17156探讨了用计数排序进一步提升排序速度的可行性。讨论结束我们一致认为使用哈希表将大大提高程序的效率。
使用哈希表后,结果如下:
可以看到在使用哈希表后,程序的执行速度有了显著的提升。这是因为词频统计算法的复杂度从O(n^2)降到了O(n)的缘故。
通过本此作业实践,我体会到软件开发需要与软件测试同时进行,没有软件开发就没有测试,软件开发提供软件测试的对象。软件开发和软件测试都是软件生命周期中的重要组成部分,都是软件过程中的重要活动。而软件测试是保证软件开发产物质量的重要手段。通过测试,我们可以尽早发现软件缺陷,并确保其得以修复;最后,完善的测试为软件可靠性与安全性评估提供了重要依据。