软工实践寒假作业(2/2)

	项目地址:https://github.com/Fzucaoxin/PersonalProject-Java.git

	代码规范:https://github.com/Fzucaoxin/PersonalProject-Java/blob/main/221801112/example/codestyle.md

一、作业描述

作业课程 2021春软件工程实践|S班 (福州大学)
作业要求 软工实践寒假作业(2/2)
作业目标 阅读《构建之法》并进行思考。完成词频统计的开发, 测试。利用git管理项目。
其他参考文献 廖雪峰的git教程源代码管理.gitignore配置语法完全版

二、目录

三、任务一

3.1.《构建之法》思考与提问

  1. .我阅读了 12章 用户体验的关于短期刺激和长期影响的内容:“在实验室里;大家心里想着,我要品尝饮料啦!漱口之后,品尝几口或一听饮料。反馈是:新产品甜味较大,口感很好,我喜欢!
    在家里:美国消费者一次买一箱(24听),随意坐在沙发里,一边看电视一边喝。反馈是:新产品甜味较大,喝多了太腻味,喝不下去,再也不买了!”。

    思考:例如一个游戏(如农药,吃鸡等),当人们热衷于它时,能够获得短期的快感,游戏公司开发了防沉迷,是为了减低长期的负面影响。但对于某个不知名的游戏,不言而喻其短期刺激显然更为重要。所以我的疑惑是:短期刺激和长期影响是一个软件不同时期应该考虑的事,还是一开始就应该一起考虑?

  2. 我阅读了 8章 需求分析 关于获取和引导需求:“需求不仅来自外界,还可以来自软件企业本身。一个免费的互联网服务到达>.定规模后,企业就会考虑如何让这个服务带来收入。例如一个免费的互联网电子邮件服务会考虑对用户收费,支持几种不同等级的用户,在邮件中附带广告,或者在页面显示广告,等等。”

    思考:现在一些小程序如嵌入至qq中的小程序,大多没有一定用户数量和规模,但程序内部依然包含大量的广告,这不是本末倒置了吗?用户需求都没有先得到满足,但是存在即合理。这些软件程序依靠什么在市场上能够继续生存?

  3. 我阅读了 8章需求分析(p158) 关于提高估计能力的招数:"实际花费取决于两个因素--多某件事的估计时间X,以及他做过类似开发工作的次数N:Y=X +/- X/N。当N等于1时,一项工作的估计的实际花费范围是[0,,2X]"。

    思考:实际开发工作中时间不可能为0,此处去左边取闭区间,是否不妥?还是我没有理解其真正含义?

  4. 当出现不容易复现但又存在的bug时如何解决?我阅读了在阅读第一章第9页:“

    不可见性( Invisibility )

    软件工程师能直接看见源代码,但是源代码不是软件本身。软件以机器码的形式高速运行,还可能在几个CPU核上同时运行,工程师是“看”不到自己的源代码如何具体地在用户的机器上被执行的。商用软件出现了错误,工程师可以看到程序在出错的一瞬间留下的一些痕迹(错误代号、大致的目标代码位置、错误信息),但是几乎无法完整重现到底程序出现了什么问题。当工程师回过头来看源代码时,它们还是安静地排列在屏幕上。“

    思考:回想起曾经的debug方法,输出查看日志,输出函数调用栈等。又通过网络查询到,例如bug间歇性随机出现时,可分析:

    1:随机性数值导致。

    2:特定的执行过程导致的特殊情况。

    3:野指针导致的不稳定性问题。

    想请问实际工作中出现这种不可见性时,如何解决?

  5. 如何判断”过早优化“?判断一个优化是否为当前必要的?我阅读了第三章53页,原文:”过早优化:既然软件 是“软”的,那它就有很大的可塑性,可以不断改进。放眼望去,一个复杂的软件似乎很多模块都可以变得更好。一个工程师在写程序的时候,经常容易在某一个局 部问题上陷进去,花大量时间对其进行优化;无视这个模块对全局的重要性,甚至还不知道这个“全局”是怎么样的。这个毛病早就被归纳 为“过早的优化是一切罪恶的根源”。

    思考:编写程序时遇见这样的情况,事先不容易判断是否需要优化,如果当前不进行优化,最后发现程序 进 行这样的优化是必须的,而又不得不修改大量代码。这种情况如何判断“过早优化”?

3.2有趣故事:

自由软件之父的Richard Matthew Stallman(简称 RMS),留着一头卷发和满满的胡子,演讲后的问答坐在台上脱下袜子,赤脚自在的回答提问。资工系的学生问他,目前业界都使用非自由软件,自由软件也没有商业模式,想生存下去该怎么办?Stallman回答,「你可以转行」。

跟Stallman接触过的人多少会感受到他的固执脾气,约访的时候,Stallman要求记者看完他列出的10个连结,并且要编辑及记者保证不犯两个错误。

第一,绝对不会把自由软件和开放源码(Open Source)两个概念混为一谈,以及要用「GNU/Linux」称呼操作系统,而非Linux。

专访的时候,Stallman一边卷着头发,一边徐徐回答记者的提问,他的思路清晰,回答直接、锐利,但他的态度平和,甚至担心记者跳题之后会忘了回来问问题。

问他网络科技产业的未来?

他耸耸肩说,「我不知道未来会怎样,我只想现在!我为自由而奋斗,而是否能够成功,取决于大家是否加入战斗。我所能做的就是鼓励人们聚焦在『自由』,而不是社会所鼓励人们选择的其他次要价值。」

启示:当一个人有了目标和追求时,并且向着这个目标不断前进时,这个人就会变得越来越强大,做人处事有自己的原则,而这些又反过来促进一个人的前进。RMS有对自由软件的追求,这些使得他获得成功,我们做一件事时也应该有自己的目标,这样才能把一件事完成的更好。

四、任务二:

4.1 项目地址:

项目地址:https://github.com/Fzucaoxin/PersonalProject-Java.git

4.2 PSP表格:

PSP2.1 个人开发流程 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 410 840
• Estimate • 估计这个任务需要多少时间 410 840
Development 开发 350 720
• Analysis • 需求分析 (包括学习新技术) 60 120
• Design Spec • 生成设计文档 20 40
• Design Review • 设计复审 20 40
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 20 40
• Design • 具体设计 70 140
• Coding • 具体编码 80 160
• Code Review • 代码复审 20 40
• Test • 测试(自我测试,修改代码,提交修改) 60 140
Reporting 报告 60 120
• Test Repor • 测试报告 20 40
• Size Measurement • 计算工作量 20 40
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 20 40
合计 410 84

4.3 解题思路描述

需求是对文件内容进行统计,需要文件和流相关知识,起初通过简单的字节流读取文本,发现效率太低,又学习了缓冲流,提升了效率。在这过程中,又学习到了Java的NIO 读取文件内容方式,但在本项目实际效果并不如缓冲流,可能是使用的场景不适合吧,有待继续学习。

其次需要对字符串继续分析。统计单词频数,存在key和value的关系,此处可以使用map这个数据结构,对于字符串,起初使用。对于统计非空白字符的行,使用readline进行判断。起初,没有学习到JAVA Pattern和MAtcher结合正则表达式,处理字符串的高效,使用了太多分支,造成测试数据到达一定量(自己测试了字符数量:5*10^7),耗时格外的长。

4.4 代码规范制定链接

代码规范:https://github.com/Fzucaoxin/PersonalProject-Java/blob/main/221801112/example/codestyle.md

4.5计算模块接口的设计与实现过程

4.5.1 流程图

流程图

4.5.2 关键代码

  1. 统计字符数(Lib.getCharNum()):ASCII码与UTF-8编码,一个英文字母(不分大小写)占一个字节的空间(详情)。由于只对英文字符进行统计,故使用如下获得字节数(当前情况下即字符数):
	"characters: " + String.valueOf(file.length()) + "\n";
2.统计有效行数:一开始想使用
lineNumberReader.skip(fileLength);
int lines = lineNumberReader.getLineNumber();
	但是这样却不能去除空白行。最后只能一行行判断:
  FileReader fr = new FileReader(file);
  //利用缓冲区提升读取性能
  BufferedReader br = new BufferedReader(fr);
  LineNumberReader lnr = new LineNumberReader(br);
  String str;
  while ((str = lnr.readLine()) != null)
  {
  //统计包含非空白字符的行
  if (!(isBlankString(str)){
  	lineNum++;
  }
  1. 单词总数与单词频数(根据数量和字典序排序)统计

    存在key和value的关系,故使用map。忽略key的大小写敏感,可以使用TreeMap,构造函数传入String.CASE_INSENSITIVE_ORDER比较器。但是TreeMap效率没有hashMap 高,故综合考虑使用hashMap。此外利用Pattern,Matcher通过正则表达式,对字符串进行判断处理:

    String[] strs = s.split("[^a-zA-Z0-9]");
    Pattern pattern = Pattern.compile("^[a-z]{4}[a-z0-9]*");
    
    for (String str : strs)
    {
        str = str.toLowerCase();
        matcher = pattern.matcher(str);
    
        if (!str.isEmpty()&&matcher.find())
        {
            cnt++;
            Integer t = map.get(str);
            if (t == null)
            t = 0;
            map.put(str,1+t);
        }
    }
    

    对map的排序这是先将其转为list,利用Collection.sort()进行排序

    //将hashMap转化为list
    List<Map.Entry<String,Integer>> list = new ArrayList<>(map.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());
        }
    });
    

    最后再利用循环,对map 中的值进行统计即可得到答案。

4.6 计算模块接口部分的性能改进。

改进程序前的所有对象的实时内存:

改进程序前的所有对象的实时内存

改进后所有对象的实时内存:

改进前耗时:

改进前耗时

改进后耗时:

改进后耗时

由以上对比知改进后消耗了更多的内存空间,但是执行时间得到了大幅缩短。

改进思路:

1.利用BufferReader读取文件,增加读取速度。
2.利用正则表达式(java Pattern和MAtcher),取代原来多个if else 分支对字符串的判断与处理。
    3.用StringBuilder代替String,加快字符串拼接的速度。

4.7 计算模块部分单元测试展示。

4.7.1 单元测试代码

循环调用进行11次测试(10份测试数据,一份测试找不到文件异常)。
for (int i=0;i<11;i++)
    if (i==0)  		  	
        testCount("testData/answer.txt","testData/output.txt","testData/input.txt");
    else
        testCount("testData/answer"+i+".txt","testData/output"+i+".txt","testData/input"+i +".txt");
将程序得到的结果与期望结果answeri.txt的进行一行行比对,给出结果不一样的行:
for (int i=0;(str2=buf2.readLine())!=null;i++)
{
    //读取的内容
    str1=buf1.readLine();
    Assert.assertEquals("第"+(i+1)+"行没有获得预期的结果",str1,str2);
}

4.7.2 测试覆盖率截图

4.7.3 构造测试数据的思路

1.检验单词输出顺序:

windows95,windows98 windows2000 windows20002.

2.区别file123 和123file以及aaa是否为单词。

3.插入空白行。

4.单词分隔符不仅仅加入空格,还有其他如;,:等字符。

5.使用程序产生10^7量级的数据进行测试。

4.8 计算模块部分异常处理说明。

  1. FileNotFoundException异常,不存在该或找不到文件。

     单元测试样例:如4.4.1。
    
  2. IOException 异常。可能出现与权限不够或者读取文件时,文件被删除等。

4.9 心路历程与收获

  1. git 学习:在git学习的过程中,了解到git的源于Linus与版本控制系统BitKeeper东家BitMover公司的一段往事,Linus随即花费两周时间写了一个分布式管理系统--git。历史是我们未来发展的参照物,我们不一定要成为像Linus这样的人物,但是我们可以以此勉励自己。在本次项目版本的多次commit 过程中,一开始以为只需要add,commit,push,然后意外发生了,我发现中间某一次commit不符合要求,于是我又去学习相关git知识,终于解决了问题,或许学习就是这样反反复复的过程。

  2. 作业完成:在对作业分析的过程中,一开始感觉要求很多不知道如何下手,后面多次阅读了作业要求,选择一部分一部分的完成作业的要求,在对照其他自己忽略的要求,完善作业。在项目的编写过程中,一开始觉得项目需求不是很难,后面逐渐发现有很多重要的东西自己平时编写程序时没有注意的,自己也花费很多时间完善这方面的知识和进行实际,如:编程规范(自定义注释等),自动化测试(编写程序生成测试数据,单元测试等),性能测试(通过分析内存占用,函数调用树等优化代码)。

  3. 其他:自己在完成项目后,发现估计的时间和花费的时间差距明显。后面又再次阅读《构建之法》工作量估计的内容,公式法,扑克牌法等。以后需要更多的将这些方法运用于实践,来增加自身的估计能力。

posted on 2021-03-04 16:42  明月何时有  阅读(128)  评论(6编辑  收藏  举报