寒假作业2/2
目录
基本信息
这个作业属于哪个课程 | 2021春软件工程实践|W班 |
---|---|
这个作业要求在哪里 | 作业要求 |
这个作业的目标 | 阅读《构建之法》提出问题,编写词频统计程序 |
阅读《构建之法》并提问
问题一
结对编程
在《构建之法》第四章中:
“在结对编程模式下,一对程序员肩并肩、平等地、互补地进行开发工作。驾驶员:控制键盘输入。领航员:起到领航、提醒的作用。驾驶员和领航员不断轮换角色,领航员要控制时间。只有水平上的差异,没有级别上的差异。”
如果结对编程的两个人,在水平上有高低之分,那么是高水平的人更多作为驾驶员,还是低水平的更多作为驾驶员。换句话说,驾驶员和领航员哪一个需要更高的水平?
第一次接触这种思想。现实生活中类似的搭档关系,如飞机驾驶员和副驾驶,战场上的狙击手和观察员,这些搭档一般不会互相轮换角色,但是结对编程中的两个人却可以不断轮换角色。但是我认为这样一对组合还是会有大体分工的,一个人更多的作为驾驶员,而另一个人更多的作为领航员。
问题二
提高产品竞争力
在《构建之法》第十六章中:
“不同的产业需要不同的组合拳,最好是一个完整的套路,对于最近几年比较火热的互联网产业来说,可以参考下面的建议。第一步,了解团队能力、产品方向和大环境的趋势;第二步,选择合适的细分市场。”
对于提高产品的竞争力应该选择与众不同,还是迎合市场呢?
面对广大的市场,单纯的创新明显不够,还要与市场接轨,了解目标群众的喜欢习惯,从而来提高自己的影响力,但这样也就容易淹没在人群中。那么到底是应该选择与众不同,还是迎合市场呢?我认为在迎合市场的同时又有一定程度的创新,与同类型其他产品有吸引用户的地方,这样才能将产品竞争力最大化。
问题三
关于创新
在《构建之法》第十六章中:
“原始布局设计的优点失去了原有的价值,反而变成了弱点。但是,长期以来,人们已经习惯了QWERTY键盘,所谓先入为主。”
在创新的方法和已经有前人经验的方法应该如何选择?
我不否认先入为主的概念对人的影响确实很深。对于某个问题,有一种你熟悉的效率较低的方法,和你不熟悉的高效方法,大多数人会选择之前先学会的较为麻烦的方法,因为先入为主。或者一开始用低效的方法但没有及时改正,之后再遇到同类型的题的时候,你脑海中浮现的还会是那种解法。但是为了提高做题效率,为了提高正确率,你不得不改变先入为主的那些做法,选择更好的更正确的那些方法。时代是发展的,你永远不知道以后流行的是什么。所以我还是认为创新才是未来,只是可能时机未到,或者没有坚持或者改变的决心。而且既然是要创新,就必然要打破固有的僵局。
问题四
软件泛化
在《构建之法》第三章中:
“有些软件本来是解决一个特定环境下的具体问题,有的程序员一想, 我们能不能做一个平台,处理所有类似的问题,这样多好啊!这样的前景的确美妙,程序员的确需要这样的凌云壮志,但是要了解必要性、难度和时机。“画扇面”就是一个很好的例子。”
软件到底应不应该泛化?
关于软件的泛化在我们的生活中应该是特别常见的,特别是我们国内的一些软件,比如腾讯的qq啊,阿里的支付宝等等等等,基本什么平台都会从一开始特别纯净的做它的专项,慢慢的就开始多了很多乌七八糟的东西。qq和支付宝就是给我这种印象最深的两个软件,特别是支付宝,明明是一个支付软件,按理来说以方便支付为目标,打造一个纯净的支付平台才是正确的,但是现在的支付宝除了本该有的功能外,多了各种乱七八糟的广告,什么购物消息,商家推送,甚至还可以在上面浏览外卖,电影票等等。这真是让我特别头痛的,我觉得这样的泛化让软件太杂乱了,反而让最开始的支付功能不那么好用。
问题五
用户体验与产品质量
在《构建之法》第十二章中:
“好的用户体验当然是所有人都想要的,如果它和产品的质量有冲突,怎么办?牺牲质量去追求用户体验么,用户能接受么?”
如何平衡用户体验和产品质量?
看了后面杰克·韦尔奇的故事,我在想,对于一个产品来说用户体验和产品质量哪一个更重要?如果都需要,那么应该如何能找到他们之前的平衡点呢?网上有些的说法是这样的:“用户需求是根本,在用户体验这个问题上,还要特别考虑到短期刺激和长期影响,在必要的时候我觉得可以牺牲软件质量去追求用户体验。”我认为这也确实是有道理的,只有吸引用户,并且留住用户,产品才有继续发展下去的可能,但是侧重用户体验也不是完全放弃质量,那么这个最佳平衡点是可计算的吗,应该如何计算呢?
WordCount
Github项目地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
• Estimate | • 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | ||
• Analysis | • 需求分析 (包括学习新技术) | 60 | 120 |
• Design Spec | • 生成设计文档 | 30 | 20 |
• Design Review | • 设计复审 | 10 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
• Design | • 具体设计 | 30 | 60 |
• Coding | • 具体编码 | 300 | 600 |
• Code Review | • 代码复审 | 30 | 100 |
• Test | • 测试(自我测试,修改代码,提交修改) | 30 | 200 |
Reporting | 报告 | ||
• Test Repor | • 测试报告 | 15 | 30 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 570 | 1215 |
代码规范链接
https://github.com/6586744/PersonalProject-Java/blob/main/221801303/codestyle.md
解题思路
首先读题获得题目的几个关键点:
统计字符、行数、单词数
词频最高的10个词需要打印并计数
通过文件I/O进行输入输出
文件通过输入字符串打开
对java文件的i/o复习之后,很快得到了统计字符和行数的方法,用read和readline读取后进行++操作即可,难点在于如何统计单词数,一开始打算用ASCII码来判定两端,但这种方法不仅效率低,而且准确性也存在很大的问题。在网上寻找解决方案后最终得到了用正则表达式检测词语存入map统计的方法。
设计与实现过程
整个程序分为三个类,主程序WordCount,控制程序如何运行的Control,以及解决问题的方法类Lib。
在主程序中,仅仅只是传入了输入输出的文件路径,然后调用了Control进行程序内容的运行,这样做的目的是为了将方法封装。
public class WordCount {
static Control con = new Control();
public static void main(String[] args){
con.set(args[0],args[1]);
con.run();
}
}
Control的内容也很简单,就是按照想要统计的内容的先后顺序去调用Lib中的方法即可。
public class Control {
public File in;
Lib cou = new Lib();
public void set(String i,String o){
in = new File(i);
cou.set(o);
}
public void run(){
cou.charcount(in);
cou.linecount(in);
cou.wordcount(in);
}
}
接下来就是Lib中具体实现功能的方法了。
首先是统计字符的数量,使用BufferedReader的read方法,每次读取计数+1。
FileReader fr = new FileReader(file);
BufferedReader bfr = new BufferedReader(fr);
while((char)bfr.read()!=(char)-1){
count++;
}
然后是统计行数,方法和统计字符差不多,使用BufferedReader的readline方法,每次读取计数+1。
FileReader fr = new FileReader(file);
BufferedReader bufr = new BufferedReader(fr);
while (bufr.readLine() != null) {
count++;
}
统计单词数以及每个单词出现的次数,这个较为困难一些。还是依靠BuffereReader讲数据从文件中读出,将读出的内容保存到一个StringBuffer中,因为单词统计不区分大小写,所以调用toLowerCase方法将其全部转化为小写,然后通过Pattern和Matcher类以及正则表达式挑选出符合条件的单词,每找到一个符合条件的单词则单词总数+1。然后将单词和该单词出现的次数存入一个Map<String,Integer>中,若该单词第一次出现,令次数=1,若不是第一次出现则取出原有次数+1。
FileReader fr = new FileReader(file);
BufferedReader bfr = new BufferedReader(fr);
StringBuffer stb1 = new StringBuffer();
String line = null;
while((line = bfr.readLine()) != null) {
stb1 = stb1.append(line);
}
String stb=stb1.toString().toLowerCase();
Pattern pattern = Pattern.compile("[a-zA-Z]{4,}+[0-9]*");
Matcher matcher = pattern.matcher(stb);
Map<String, Integer> map = new TreeMap<String, Integer>();
String word = "";
Integer num = null;
int count = 0;
while(matcher.find()) {
word = matcher.group();
count ++;
if(map.containsKey(word)) {
num = map.get(word);
num += 1;
}
else {
num = 1;
}
map.put(word, num);
}
性能改进
对一个较大的数据进行的性能测试
性能提升的改进来自于单词算法的改变,使用正则表达式
单元测试
因为我写的方法是没有返回值是直接写出到文件的,所以为了测试方便,我暂时修改了一下方法,让其返回结果。
字符统计测试
测试数据:***~~~123scwa\//^&2
@Test
public void charcount() {
int num=20;
a.set("output.txt");
assertEquals(a.charcount(name),num);//测试通过
}
行数统计测试
测试数据:
123
123123
123
123
123
@Test
public void linecount() {
int num=8;
a.set("output.txt");
assertEquals(a.linecount(name),num);//测试通过
}
单词数量统计测试
因为我的单词数量统计和词频的显示是在一个函数里的,并且函数是直接将结果写到文件。所以我使用了前面性能测试大数据时的结果,测试的原数据我放到下面。
测试数据:测试数据
结果是没有问题的
关于覆盖率
覆盖率没有满的部分是catch块
异常处理说明
异常的情况:
没有输入两个文件路径或者文件路径有误会抛出异常
都是抛出printStackTrace()
心路历程与收获
git的话之前之前一直有使用过,不过由于是个人项目或者团队所有人都有权限的项目,关于fork和pullrequest的部分还是第一次接触,git是一个很方便的工具,也确实是还有很多便捷的功能需要去学习。
关于这次项目的心路历程和收获的最多的应该是代码测试这一块。由于完全的未知从了解到安装就已经花了我大把的时间,到开始测试,一开始听不明白为什么要做这件事,但是一段时间后,结合着将方法封装,与这里将每个方法作为一个接口来运行,也算是悟到了一些东西,可能是这个项目的作用不是很明显,但是像web里测试接口那样,这种将接口独立出来跑的方法能帮助我们更快更高效的找到这个方法中的错误,这种细分的方式也提升了修复错误的容易性。