软工实践寒假作业(2/2)
这个作业属于哪个课程 | <2021春软件工程实践|S班> |
---|---|
这个作业要求在哪里 | <作业要求> |
这个作业的目标 | 1.重读《构建之法》并提问 2.学习github和git的使用 3.编写词频统计程序 4.学习并进行单元测试 |
其他参考文献 | 1.正则表达式的学习 2.git和github的学习 3.邹欣:关于单元测试和回归测试 4.IDEA中使用JUnit4 |
作业一 阅读《构建之法》并提问
问题1:“足够好”的软件
在课本P15中写着:一些同学认为,所谓好软件,就是软件没有缺陷(Bug),所谓软件工程,就是把软件中的Bug都消灭掉的过程。P17中写着:“足够好”的软件不是期末前两天由两三个同学熬通宵赶出来的急就章,而是经历了一定的软件流程,通过全体团队成员的努力,在一个长期阶段内逐步完成的。
一个软件的好坏应该更主要由什么来评价,评价标准是怎样呢?通过搜集资料发现软件的好坏得到的大多是来自于用户的评价。我的想法:我觉得评价应该注重的是用户评价,看软件的受众是什么,将自己代入受众的角度去体验软件,如果舒适度便捷度等都合理,那么软件应该就挺好的。
问题2:注释及可移植性
在课本P74中写着:注释应该只用ASCII字符,不要用中文或其他特殊字符,否则会极大地影响程序的可移植性。
像在于现阶段,我对于很多注释还是采取使用中文的方式,此时的我的认知中并无法体会到这之中的影响。我想知道在之后的工作中是否每个人的注释基本都采用英语,或者是什么样的情况。再对于文中提及的可移植性。注释对于每个函数都有必要添加吗,比如一些工具类的小函数,还是应该怎么选择需要注释的地方。
问题3:代码复审
在课本P79中写着:软件工程中最基本的复审手段,就是同伴复审。
当局者迷,旁观者清。我理解了同伴复审能够看到更多自己看不到的错误或者遗漏,这有利于效率的提高。但就大学里所遇到的来说,我对于别人的代码常常会遇到看不懂的情况,或者代码风格差异较大的情况,我们对于同伴的选择是否优先考虑水平接近,代码风格接近。对于以后进入公司后我想大多应该是团队任务,在那里对于代码复审的情况又是如何呢?
问题4:创新思维
在课本第16章“IT行业的创新”中写着:最近几年,我们整个社会对创新都很感兴趣,媒体上充斥着创新型的人才、创新型的学校、创新型的公司、创新型的社会等名词,有些城市还把创新当做城市精神之一,还有城市要 批量生产上千名顶级创新人才。
现在国内对于创新十分重视,由“中国制造”转向“中国创造”。IT行业对于创新思维的要求相对会更高,那么在平常中该如何去提高这种思维方式。我的理解是从需求中出发进行创新,还有别的方法吗?我认为对于创新的诞生,主要还是从需求出发,因为有需求,为了满足这种需求而创造出更便捷的方式。还有就是平常生活中要多关注客观事物的不同性与特殊性。
问题5:提高商品竞争力
在课本P373的魔方的创新故事中,果冻从其他地方带来魔方,这是创造了一个原本这个地方没有的事物;小飞复印了魔方口诀表来提高自己的竞争力,大牛通过改变游戏规则来先做到别人不能做到的事,但最后都是因为同学们对魔方的兴趣渐渐失去而失去了市场。
这三位同学的行为也对应了不同的选择:1.去封闭的地方卖魔方,那里的人不知道外面的世界。2.依靠自己别的优势或垄断。3.开发有差异化的新东西,体现独特的价值。对于一个事物大众化的过程中(很多人都能够实现),还有什么方法能够提高它的竞争力。
附加题:软件工程发展的过程中有什么你觉得有趣的冷知识和故事?
一次,冯·诺伊曼在晚会上,女主人勇敢地向他提出一个谜题:两列火车在同一轨道上以每小时 30 英里的速度相对而行,且相距 1 英里,这时栖在一列火车前面的一只苍蝇以每小时 60 英里的速度朝着另一列火车飞去。当它飞到另一列火车时,它又迅速地飞回来。它一直这样飞过去飞回来,直到两列火车不可避免地发生碰撞。问这只苍蝇共飞了多少英里?几乎在女主人刚解释完问题的同时,冯·诺伊曼就答道:“1 英里。”“太让我惊讶了,你这么快就算出来了。” 她说道。“大多数数学家都没能看出这里面的技巧,而是用无穷级数去计算,这花费了他们很长时间。”“什么技巧?我也是用无穷级数算的。” 冯·诺伊曼回答道。参考来源
这个故事有着很多版本,冯·诺伊曼从小就被寄予厚望,他也为计算机行业做了许多贡献。
任务二 WordCount编程
0.作业描述
在大数据环境下,搜索引擎,电商系统,服务平台,社交软件等,都会根据用户的输入来判断最近搜索最多的词语,从而分析当前热点,优化自己的服务。首先当然是统计出哪些词语被搜索的频率最高啦,请设计一个程序,能够满足一些词频统计的需求。
1.Github项目地址
作业仓库:PersonalProject-Java
个人github项目地址:https://github.com/camocd/PersonalProject-Java
2.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 40 |
Development | 开发 | 600 | 755 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 200 |
• Design Spec | • 生成设计文档 | 30 | 25 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 15 |
• Design | • 具体设计 | 50 | 60 |
• Coding | • 具体编码 | 300 | 360 |
• Code Review | • 代码复审 | 20 | 15 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 70 |
Reporting | 报告 | 135 | 145 |
• Test Repor | • 测试报告 | 90 | 100 |
• Size Measurement | • 计算工作量 | 15 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 35 |
合计 | 765 | 940 |
3.解题思路描述
- 刚开始看到题目的时候,对于github和git的使用是比较少的,这也给我带来了一些困扰,不过通过查找一些资料后,也渐渐会使用了,也感受到了其中的便利。
- 编程作业,首先从题目中提取重要信息
- 输入、输出文件以命令行参数传入
- 输入文件只考虑Ascii码,不考虑汉字
- 空格,水平制表符,换行符,均算字符
- 任何包含非空白字符的行,都需要统计
- 只输出频率最高的10个单词,形式均为小写格式
- 输出时冒号后面包含一个空格
- 对编程任务进行分解
- 读取input文件中的数据
- 统计字符数
- 统计单词数
- 统计行数
- 统计各单词的出现次数
- 将各种结果输出到output文件中
- 在编程设计的时候考虑到的问题
- 应该用什么读取文件中的数据
- 单词可以用String类的split拆分
- 应用什么来判断分解出来的字符串是一个单词
- 该怎么判断行
- 单词及其频数用什么存储可以方便输出(Map类)
4.代码规范制定链接
5.设计与实现过程
只有两个类,WordCount类和Lib类,用WordCount类来执行Main函数,命令行参数传入文件名,对于传入文件后的统计和输出的处理都在Lib类里写。Lib里主要包含readFile
、writeFile
、charNumCount
、wordNumCount
、lineNumCount
几个函数
- 读取文件
public String readFile() {
StringBuilder builder = new StringBuilder();
try {
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
int ch;
while ((ch = reader.read()) != -1){
builder.append((char)ch);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return builder.toString();
}
读文件是选择采用BufferReader类的read方法,存入StringBuilder对象获取整个文件后再做处理
- 统计字符数
String str = readFile();
charNumber = str.length();
直接由字符串的length函数获得
3.统计行数
String[] lines = str.split("\n");
lineNumber = lines.length;
Pattern linePattern = Pattern.compile("\\s*");
for(String line : lines){
Matcher matcher = linePattern.matcher(line);
if (matcher.matches()){
lineNumber--;
}
}
先通过split("\n")
将文件分成多行,再通过正则表达式\\s*
去掉那些只含有空白字符的行,得到需要的值。
- 统计单词数
String[] words = str.split("[^a-zA-Z0-9]");
Pattern wordPattern = Pattern.compile("([a-zA-Z]{4}[a-zA-Z0-9]*)");
for (String word:words){
Matcher matcher = wordPattern.matcher(word);
if(matcher.matches()){
wordNumber++;
String w = word.toLowerCase();
Integer count = wordsMap.get(w);
if(count == null){
count = 0;
}
wordsMap.put(w,count+1);
}
}
通过正则表达式[^a-zA-Z0-9]
将文件分成一个个词,在通过正则表达式([a-zA-Z]{4}[a-zA-Z0-9]*)
判断是否为满足条件的单词。若为单词,将单词存入Map里,进行统计。
- 统计各单词的出现次数
在统计单词数的时候已经将单词及出现次数存在Map里,但是输出时要满足一定的顺序,通过查资料知道所用HashMap是无序的,则需要进行排序处理
list = new ArrayList<Map.Entry<String, Integer>>(wordsMap.entrySet());
Collections.sort(list,new Comparator<Map.Entry<String,Integer>>(){
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if(o1.getValue() == o2.getValue()){
return o1.getKey().compareTo(o2.getKey());
}
return o2.getValue().compareTo(o1.getValue());
}
});
通过重写compare方法来对Map进行排序
- 输出文件
用BufferWriter类的write方法进行输出
6.性能改进与测试
- 读写文件的时候对比采用BufferReader和BufferWriter类
带有缓存的读写类能够减少操作系统访问文件的次数,加快程序的运行速度。
输入测试input.txt
相应输出结果ouput.txt
运行时间
7.单元测试
- 对字符计数进行测试
@Test
public void getCharNumber() throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("x.txt"));
String text= "ads54\n\t\rdasd,";
writer.write(text);
writer.flush();
writer.close();
Lib lib = new Lib( "x.txt ", "y.txt");
lib.writeFile();
Assert.assertEquals(lib.getCharNumber(),13);
}
- 对单词计数进行测试
@Test
public void getWordNumber() throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("x.txt"));
String text= "hello1,2hello2,hello3,123jki2,iii\ndasd,156ads";
writer.write(text);
writer.flush();
writer.close();
Lib lib = new Lib( "x.txt ", "y.txt");
lib.writeFile();
Assert.assertEquals(lib.getWordNumber(),3);
}
- 对行数计数进行测试
@Test
public void getLineNumber() throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("x.txt"));
String text = "124\n12312\n\n\n\n3123\n\n ";
writer.write(text);
writer.flush();
writer.close();
Lib lib = new Lib( "x.txt ", "y.txt");
lib.writeFile();
Assert.assertEquals(lib.getLineNumber(),3);
}
- 覆盖率截图
覆盖率未满为trycatch异常处理
8.异常处理说明
主要是对于文档打开操作的异常处理
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
} catch (IOException e) {
System.out.println("文件打开失败");
e.printStackTrace();
}
- 命令行参数数量不正确的处理
if (args.length != 2){
System.out.println("命令行参数应该为两个");
return;
}
9.心路历程与收获
- 刚看到作业的时候,心情是很忐忑的,因为这次作业的内容需要通过github和git来完成,对于之前,也只是偶尔从github上下载东西,对于使用倒是一窍不通,还好通过学习,现在已经会使用一些了,也能够体会到git的方便。还有就是对单元测试的学习,基本算是会运用了。
- 而且一段时间较少使用Java编程了,通过这次作业,也重新温习了一些运用,也加深了对Map类的运用。
- 也逐渐地认识到了自己的代码规范,好的代码规范可便于编程,有利于编程。
- 还有就是对于时间的规划,我对于时间的规划还存在一定的问题,对时间有规划也能够提高自己的效率,会不断学习提高自己。
- 复习了正则表达式,这次作业也让我对于正则表达式有了更多的运用,使用起来也更加熟练