寒假作业2/2
这个作业属于哪个课程 | 2021春软工实践|W班 (福州大学) |
---|---|
这个作业要求在哪里 | 寒假作业2/2 |
这个作业的目标 | 1.阅读《构建之法》并提问 2.WordCount编程 |
其他参考文献 | CSDN |
part1:阅读《构建之法》并提问
提问
1.书的第三章第一节讲到了个人能力的衡量与发展,其中提到初级软件工程师的成长有一方面体现在
对通用的软件设计思想和软件工程思想的理解。
在这一方面我认为衡量起来缺少一定的指标衡量标准,不像之后的对于实际项目的衡量可以通过大小、耗时、质量和错误等指标进行衡量。在网上查询相关资料后,我了解到软件工程思想包括:1.项目计划和质量管理;2.可行性分析和需求分析;3.系统设计;4.面向对象程序设计;5.测试和改错;6.维护和再生工程等多个方面。但个人对于如何在实际工程中加强软件工程思想认识还是缺少方向。
2.书3.3节中作者提到了他通过口诀玩魔方的故事。其中有几句作者的独白让我陷入思考
后来我想,把第二层拼好,我只知道找到某个模式,按照某个口诀执行即可。但是我并不了解为什么这个口诀能把第二层拼好,同时不打乱第一层的结果。我更不知道如果在在执行中走错了几步,如何随机应变,挽回局面……我的魔方技能应该是“独立还原一面,其他看口诀可搞定。”那么我这阵势的技能还值得写进简历么?
这段话在我看来很真实,我现在的状态似乎也是看上去能够根据给定的需求踉踉跄跄完成一个程序。当然,其中有自己独立创造的,也有参考网上大牛的代码,这和作者写的“其他看口诀可搞定”似乎很神似。到底达到什么程度我才可以自信的在简历上写“较好的掌握某种语言或者编程技巧”呢?在团队中执行一项项目时应该执着于创新从而掌握技巧还是根据“口诀”完成整个项目?
3.书4.4节中提到同伴复审,这是软件工程中最基本的复审手段。其中我对
谁来做代码复审?即最有经验、熟悉这一部分代码的人。对于至关重要的代码,我们要请不止一个人来做代码复审。
……
找出代码的错误,比如:不符合团队代码规范的地方。
这是否意味着在完成一个团队项目之前我们需要首先指定团队的关于该项目的代码规范?对于一个小型团队,可能每一个部分仅仅由一个人负责完成,那寻找“最有经验、熟悉这一部分代码的人”是否会很困难?
4.书第五章讲述了团队和流程,其中的课后问题问道
如果你领头开展一个全新的项目,你要怎么选择“合适”的团队模式?
在我看来,这些团队模式各有利弊,因此作者才会问我们要选择哪种团队模式。但是在我看来,也如书上所说,某些团队模式会互相转换和演变,例如主治医师模式运用到极点可以蜕化为明星模式,也有可能退化为“一个学生干活,其余学生跟着打酱油”的模式,因此团队模式的选择是否有必要,该由团队里的谁负责选择和维持?该怎样维护团队模式?随着时间的推移和工程进度的推进,应不应该考虑调整团队模式以适应新进度?
5.在IT行业的创新这一章节中作者提出“大家并不是都喜欢创新”这个论断
创新就是做和以前不一样的事,并不是所有的人都喜欢“不一样”。
诚然如此,在现在这个追求创新的社会,似乎“没有创新”成为了一个贬义词。但又有多少创新的人士本身卷于创新这件事本身呢?有些是苦于绞尽脑汁而不能实现创新,有些是因为创新本身有悖于“传统”,对于打破传统普通大众若是觉得威胁到当前自身利益的时候,便必然乏力阻挠。因此我们该如何审视创新这个话题?
冷知识
第一个电脑游戏出现于1962年,由麻省理工学院的计算机程序员Steve Russell与其团队一同编写,这款名为《太空大战》的游戏耗费了他们近200个小时。该游戏允许两名玩家分别控制两艘飞船,目标是击中并摧毁对方飞船,并且玩家还需要躲避屏幕中代表星球的小白点。如果玩家撞上这些星球,则游戏失败。虽然Russell和他的团队从未在这个游戏说的任何收益,但必须承认如果没有这一突破我们可能永远不会拥有如今蓬勃发展的视频游戏产业。
https://blog.csdn.net/weixin_46883950/article/details/107854234
part2:WordCount编程
github项目地址
https://github.com/BobbyShao/PersonalProject-Java
PSP表格
PSP2.1 | Personal Software Process Stage | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 5 | 12 |
Estimate | 估计这个任务需要多少时间 | 5 | 12 |
Development | 开发 | 395 | 468 |
Analysis | 需求分析 (包括学习新技术) | 60 | 88 |
Design Spec | 生成设计文档 | 20 | 15 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 15 | 25 |
Design | 具体设计 | 60 | 40 |
Coding | 具体编码 | 180 | 220 |
Code Review | 代码复审 | 10 | 10 |
Test | 测试(自我测试,修改代码,提交修改) | 40 | 80 |
Reporting | 报告 | 60 | 90 |
Test Report | 测试报告 | 20 | 40 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 40 |
合计 | 500 | 570 |
解题思路描述
程序流程:
1.读入input.txt文件
2.统计文件的字符数并输出到output.txt
3.统计文件的单词总数并输出到output.txt
4.统计文件的有效行数并输出到output.txt
5.统计文章中个单词的出现次数并输出到output.txt
- 输入输出涉及到文件流类
- 统计字符数用StreamReader
- 统计行数使用getline()方法,同时用trim().equals("")判断空行
- 考虑到统计单词数和单词词频,想提取文件中的单词将他们放入字符串中。通过正则表达式分割和判断单词合法性
- 将合法单词和词频以键值对的方式放入map中,最终排序输出
代码规范
计算模块接口的设计与实现过程
考虑到不同功能模块会使用不同的文件流类,所以在传参时均以File类型传入,套接不同的文件流。
Lib.java包括计算模块的各种函数,WordCount.java包括主函数作为程序的入口,test()函数用于构造测试用例输入到input.txt中,run()函数调用Lib.java中的各个函数方法,输出结果至output.txt中。
1.统计字符数
InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream(file));
int num=0;
try {
while(inputStreamReader.read()!=-1)
{
num++;
}
} catch (IOException e) {
e.printStackTrace();
}
return num;
使用inputstreamreader读入一个字符就将字符数加1。
2.统计单词总数
StringBuffer stringBuffer = new StringBuffer();
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
stringBuffer = stringBuffer.append(line+' ');//加上空格以分隔单词
}
}
……
String sb=stringBuffer.toString().toLowerCase();//将单词全部统一转为小写
return sb;
首先考虑将文件转化为一个字符串以便后面统计词频。因为在对字符串append的时候如果读入的是空行,在对下一行单词的读入过程中就无法与之前的单词分开,因此需要在读入后append一个' '。因为单词的大小写不会影响到单词数量,同时为了实现最后小写输出的要求,在这里先将字符串转为小写。
String[] linewords=str.split("\\W+");//匹配非单词字符后分隔
return linewords;
第二步将传入的字符串用正则表达式分隔。\W+表示匹配任何非单词字符,即遇到非A-Z a-z 0-9的字符就会进行划分。
int cnt=0;
Pattern pattern = Pattern.compile("[a-zA-Z]{4}([a-zA-Z0-9])*");//判断是否为合法单词
for(int i=0;i<linewords.length;i++) {
Matcher matcher = pattern.matcher(linewords[i]);
if(matcher.find()) {
//System.out.println(matcher.group());
cnt++;
}
}
return cnt;
最后对已经划分好的字符串使用正则表达式判断每个单词是否符合只包含字母和数字,同时以至少四个字母开头的要求。将正则表达式传入Pattern.compile()生成Pattern对象,利用Pattern对象的matcher()生成Matcher对象,对于每一个单词,如果符合正则表达式,matcher.find()会返回true,对统计单词的cnt进行加一操作。
3.统计文件行数
int linenum=0;
BufferedReader input = new BufferedReader(new FileReader(file));
String line = null;
while ((line = input.readLine()) != null) {
if (line.trim().equals("")) //该行为空行的情况
continue;
else
linenum++;
}
return linenum;
使用bufferreader的readline方法可以统计所有行数,但是对传入的行需要通过trim方法判断是否为空行,对空行不计入总行数。
4.统计单词个数
Map<String,Integer> hashMap=new HashMap<String,Integer>();
Set<String> wordSet=hashMap.keySet();
Pattern pattern = Pattern.compile("[a-zA-Z]{4}([a-zA-Z0-9])*");//合法单词
for(int i=0;i<linewords.length;i++) {
Matcher matcher = pattern.matcher(linewords[i]);
if(matcher.find()) {
String word=matcher.group();
if(wordSet.contains(word)) {//如果已经有这个单词了,
Integer number=hashMap.get(word);//从map中找到该单词value++
number++;
hashMap.put(word, number);
}
else {
hashMap.put(word, 1);//放到map中,value设为1
}
}
}
因为对于每一个接口可能会单独调用,因此这里还是以最先开始的包含合法与非法单词的字符串作为参数传入。判断每个合法单词是否已经在map中存在了,如果存在则对它的频数加一,否则将它加入map中,设置频数为1。
List<Map.Entry<String, Integer>> list = new ArrayList<Map.Entry<String, Integer>>(hashMap.entrySet());
list.sort(new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if(o1.getValue().equals(o2.getValue())) {
return o1.getKey().compareTo(o2.getKey());//value相同按照key字典序正序排序
}
else
return o2.getValue().compareTo(o1.getValue());//value不同逆序排序
}
});
对map的排序需要自己构造排序函数。首先对频次按照从高到低排序,若出现了频数一致的情况,则需要按照字典序从小到大排序。
writer.write(list.get(i).getKey()+": "+list.get(i).getValue()+'\n');
输出时首先判断list的大小是否超出10个,然后按照key-value对使用FileWriter输出到output.txt。
计算模块接口部分的性能改进
1.输入时文件仅读取一次,避免多次读入文件造成的时间浪费。同时使用缓冲流提高效率。
2.使用散列表存储单词而不是list。从而提高查找单词时的性能。
计算模块部分单元测试展示
代码覆盖率
对于代码覆盖率的优化主要可以从以下几个方面着手:1.构造异常用例;2.减少不必要的判断;3.消除重复代码;4.简化逻辑,减少分支。
单元测试
Lib.java包括计算模块的各种函数,WordCount.java包括主函数作为程序的入口,test()函数用于构造测试用例输入到input.txt中,run()函数调用Lib.java中的各个函数方法,输出结果至output.txt中。
- 对字符数统计正确的测试。测试用例中使用了大小写字母、空格、制表符、换行符等字符。每行共18个字符。
- 对文本行数统计正确的测试。测试用例中在字符串中和字符串末使用了换行符,循环五次,同时添加了5行空白行。最终行数为10行。
- 对合法单词数统计正确的测试。测试用例中每行仅有abcd123和indispensable两个单词合法。循环十次最终共有10个合法单词。
- 对单词词频统计统计正确的测试。测试用例中aaaz aaaa aaab aaad aaaf aaae aaae。其中aaae频次最高,其他单词频次相同,应该按照aaaa aaab aaad aaaf aaaz排列。
计算模块部分异常处理说明
1.在main()函数中将结果写入output.txt中可能遇到IOException,程序会提示File error!
2.在接口方法中使用try、catch包裹文件流的read()等方法,如果没有找到传入文件则会抛出FileNotFoundException。
心路历程与收获
1.一开始看到这次任务的时候其实是很慌张的。因为从一开始的github和git指令自己就没有涉及到过,再加上寒假也没怎么碰过编程,所以看到一下子涌过来的任务就有点手足无措不知道从何处开始下手。
2.于是我开始从理论和实践两个方面下手。一方面从博客和b站上学习如何操作github,一边完成布置的WordCount编程任务,并试着将每一次的改动commit到自己的仓库中。其实这次的程序只是一个封装了几个方法的小程序而已,所以难度并不是特别大,主要是对于Java的文件流有些遗忘,所以回看编出的程序还是缺少条理。其他的一些API和方法也是通过CSDN等论坛加上自己的理解解决了。
3.新学到了github的操作方法:通过自己的动手和视频的学习,我深刻感知到了git在代码管理和团队协作(虽然这次作业还没有涉及到团队的项目)方面起到的重要作用,每次上传代码我都能清楚的了解这段时间自己到底做了哪些工作。
4.开始接触正则表达式。相对于编写一连串的判断语句,正则表达式在代码的简洁度上有着无与伦比的优势,值得继续钻研。
5.意识到单元测试和覆盖率的重要性。他们不仅能够给你提供性能优化上的方向,同时可以测试代码的完整性,为我找到那些可能会产生bug的隐秘的角落。