欢迎来到Srr的博客

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

这个作业属于哪个课程 2021春软件工程实践S班(福州大学)
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 阅读《构建之法》并提出至少5个问题、完成WordCount编程
其他参考文献 CSDN、博客园、《构建之法》

1.阅读《构建之法》并提问

1.1提问

Q1:2.1.2单元测试由谁来完成?

原文P25提到单元测试必须由最熟悉代码的人,即程序的作者来写。代码的作者最了解代码的目的 特点和实现的局限性 所以,写单元测试没有比作者更适合的人选了。但是一个好的单元测试需要耗费很多的时间,而且专门的测试人员有着较好的测试思想,网上也有许多人提出开发人员虽然完成了单元测试,但结果并不理想,所以能否由专门的测试人员来完成单元测试呢?

网上查了很多资料,各有优缺。测试人员进行单元测试反复的重新理解代码需要大量的时间,反复的沟通也需要大量的成本。而且还要为后面的集成测试、系统测试等做好准备,单元测试很可能影响这些准备工作。开发人员进行单元测试会影响进度,还有就是测试效果不好。但开发人员的问题可以用工具来解决,自动化的单元测试工具,可以统计白盒覆盖,甚至提供用于找出遗漏的测试用例的工具,达到很高的测试完整性,但目前对于自动化的单元测试还是不太了解。

Q2:4.3.2可以使用goto吗?

原文P75提到函数最好有单一的出口,为了达到这一目的,可以使用goto,只要有助于程序逻辑的清晰体现,什么方法都可以使用,包括goto。但是在大一王灿辉老师的C语言课上第一次接触到goto时,老师就说能不用就不用,当时只是记下了并没有更深地去了解goto,这次在《构建之法》中再次看到,不免提出疑问。

这次仔细地去了解了一下goto语句,goto语句地危害主要在于破坏了程序结构,使程序可读性变差,大量滥用goto语句会让程序像毛线团一样复杂,但是如果只是少量的使用,为了有助于程序逻辑的清晰体现,goto还是可以使用的。

Q3:4.5.2结对编程中两人能力差异较大的话,结对编程是否弊大于利?

原文P85提到了结对编程的好处:
(1)在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。
(2)对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
(3)在心理上, 当有另一个人在你身边和你紧密配合, 做同样一件事情的时候, 你不好意思开小差, 也不好意思糊弄。
(4)在企业管理层次上,结对能更有效地交流,相互学习和传递经验,能更好地处理人员流动。因为一个人的知识已经被其他人共享。

但如果两个人的技术水平差别过大时,一人嫌另一人效率低,另一人则感觉被轻视,愈发懈怠,这又基本成了一个人的工作了,而且两人也不愉快,这样的话是否弊大于利,技术水平差距是不是结对编程的限制条件呢?

Q4:8.3在项目过程中客户突然更改需求怎么办?

原文P160讲获取用户需求时,提到了很多办法,比如面谈、调查问卷、焦点小组等,但是未提及在项目过程中客户突然更改需求的举措。我一阅读到这里就想起不久前看到的吐槽客户临时更改需求的视频。如果我们以后在工作中遇到这种情况,要怎么处理呢?

网上查了很多资料,应该在项目前期加强沟通,明确需求,合同约定允许的变更率,建立完善的需求审批流程,能避免尽量避免。但项目过程中需求变更是逃不掉的,尽量使用敏捷开发,让客户参与到开发过程中来,开发可以及时修正偏差,避免后期大面积代码重写。

Q5:团队合作中如何处理队员之间的冲突?

第五章讲到团队合作,但人一多起来问题肯定少不了。软件开发人员往往表面看似低调,其实内心骄傲,他们对自己的智力充满了自信,最无法容忍的就是自己的工作成果被否定。当团队中出现了不和谐的声音应该如何处理呢?

处理这种冲突有赖于项目主管的管理技巧以及公平的处事原则,同时把对事不对人的工作态度灌输给项目中的每个成员。冲突发生时,既要坚持原则,有理有据地作出分析,也要注意照顾双方的情绪,多做安抚工作。

1.2附加题

其实历史上的一款电脑病毒是一个误会,是一个防御专家亲自设计的。1987年的夏天,巴基斯坦俩兄弟巴斯特和阿姆捷特筹钱开了一家电脑公司,公司开张后生意异常红火,但很快有那么些人居然将盗拷后的程序性广泛传播来实现盈利,这显然损害了俩兄弟的利益。于是他们编写了一款防盗拷程序——C-BRAIN,只要有人盗拷他们的软件,C-BRAIN就会发作,将盗拷者的硬盘剩余空间给吃掉,许多盗拷软件的人电脑直接不能再存储任何东西。但是这组病毒也让他们自己头疼不已,世界各地蜂拥而至的谴责电话让他们意识到,他们可能一不留神开发出了世界上第一组破坏性电脑病毒。一般而言,C-BRAIN电脑病毒是业界公认的真正具备完整特征的电脑病毒始祖。

见解:由此可见电脑病毒的危害之大,不要随意点击不明不白的网址,以后成为程序员,保护电脑安全更是我们应该重视的。

2.WordCount编程

2.1Github项目地址

项目地址

2.2PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 20 20
• Estimate • 估计这个任务需要多少时间 20 20
Development 开发 515 970
• Analysis • 需求分析 (包括学习新技术) 90 180
• Design Spec • 生成设计文档 20 30
• Design Review • 设计复审 15 15
• Coding Standard • 代码规范 (为目前的开发制定合适的规范) 30 25
• Design • 具体设计 30 30
• Coding • 具体编码 240 420
• Code Review • 代码复审 30 30
• Test • 测试(自我测试,修改代码,提交修改) 60 240
Reporting 报告 60 150
• Test Repor • 测试报告 15 70
• Size Measurement • 计算工作量 15 20
• Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 30 60
合计 595 1170

2.3解题思路描述

刚开始拿到题目,阅读了题目要求后,可以分成六个部分

  • 将input.txt里的内容读到字符串中
  • 统计文件内容的字符数
  • 统计文件内容的单词总数
  • 统计文件的有效行数
  • 统计文件中各单词的出现次数,输出频率最高的10个
  • 将统计结果输出到output.txt文件中

对于第一部分,一开始是打算在各功能块读取文件内容,后来选择单独读取文件,将内容存到字符串,将字符串用于各统计功能模块。
对于第二部分,由于无需考虑中文,直接逐个判断是否在ASCII码范围内即可。
对于第三部分,思考了挺久,一开始是打算写个函数来判断是否为单词,后来上网查资料发现了正则表达式可以方便地匹配对应的字符串。
对于第四部分,一开始没有仔细看要求,以为直接readline就可以了,后来看到只包含空白字符的为无效行,也是用正则表达式来匹配。
对于第五部分,map的键值对可以很方便地解决这个问题,判断为是单词就可以存入map,然后排序完输出值最大的前10。

2.4代码规范制定链接

代码规范

2.5设计与实现过程

类WordCount.java

  • main() 作为程序的入口
  • readFile() 将文件内容读到字符串中
  • writeFile() 将统计结果输出到目标文件中

类Statistic.java

  • charNum() 用于统计文件字符数
  • wordNum() 用于统计单词数
  • lineNum() 用于统计有效行数
  • maxNum() 用于统计单词词频

函数关系:
首先readFile将文件内容读到字符串中,然后将字符串赋给charNum、wordNum、lineNum、maxNum四个功能函数,得到统计结果后writeFile将统计结果输出到目标文件中。

关键代码:
1、统计文件字符数
判断在ASCII码内则计入统计。

for (int i = 0; i < list.length; i++) {
    if (list[i] >= 0 && list[i] <= 127) {
        num++;
    }
}

2、用于统计单词数
用split分割字符串,用正则表达式判断每个子字符串是否为单词。

String[] words = text.split("\\W+");
for (int i = 0; i <words.length; i++) {
    String str = words[i].toLowerCase();
    Pattern r = Pattern.compile("^[a-zA-Z]{4}([a-zA-Z0-9])*");
    Matcher m = r.matcher(str);
    if (m.find()) {
        num++;
    }
}

3、用于统计有效行数
用split将每一行作为子字符串,再用正则表达式匹配,若包含非空白字符则计入统计。

String[] lines = text.split("\\n");
for (int i = 0; i<lines.length; i++) {
    Pattern r = Pattern.compile("\\S+");
    Matcher m = r.matcher(lines[i]);
    if (m.find()) {
        num++;
    }
}

4、用于统计单词词频
将单词存入map,若在map中已有此单词,则单词对应的数量加1。

if (!map.containsKey(str)) {
    map.put(str,1);
}
else {
    map.put(str,map.get(str)+1);
}

将map转为list,通过比较器实现排序,前10即为目标结果

Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
    public int compare(Map.Entry<String, Integer> pre, Map.Entry<String, Integer> last) {
        if (pre.getValue().equals(last.getValue())) {
            return pre.getKey().compareTo(last.getKey());
        }
        else {
            return last.getValue().compareTo(pre.getValue());
        }
    }
});

2.6性能改进

  • 使用BufferedReader和BufferedWriter这种带缓存的类通过缓存机制进一步增强了读写器读取和存储数据的效率。
  • 先将文件内容读到字符串,再用字符串赋给统计功能函数,不用在函数里多次打开关闭文件。
  • 在统计单词总数时判断为是单词后即可将单词存入map,不用在统计词频时再判断一次。

2.7单元测试

  • 统计字符数的测试
    测试了包含空格、数字、字母、特殊符号、换行等的字符数。
@org.junit.Test
    public void charNum() {
        String str = "  svQRFf s\nsh$%&*(hf\ns>?: ";
        Statistic tool = new Statistic();
        String test = "";
        for(int i = 0; i < 10; i++) {
            test += str;
        }
        assertEquals(tool.charNum(test), 260);
    }
  • 统计单词数的测试
    测试了空文本、大小写单词、合法非法单词、空格,非字母数字符号为分割符的文本。
@org.junit.Test
    public void wordNum() {
        String str = " aaaa bbbb&ccCC%cCcC 531sfvs \n svv \nsfbsfb123";
        Statistic tool = new Statistic();
        String test = "";
        for(int i = 0; i < 10; i++) {
            test += str;
        }
        assertEquals(tool.wordNum(test), 50);
    }
  • 统计有效行数的测试
    测试了只包含空白字符行、包含非空白字符行的测试。
@org.junit.Test
    public void lineNum() {
        String str = " aaaa bbbb&ccCC%cC\n\t\n\r\nxdfb\n\n";
        Statistic tool = new Statistic();
        String test = "";
        for(int i = 0; i < 10; i++) {
            test += str;
        }
        assertEquals(tool.lineNum(test), 20);
    }
  • 统计文件中各单词词频的测试
    测试了单词少于10个、单词多于10个、包含相同频率单词的文本、大量数据文本。
@org.junit.Test
    public void maxNum() {
        String str = "aaaa aaaa bbbb BBBB cccc dddd eeee ffff gggg hhhh iiii jjjj kkkk\n";
        Statistic tool = new Statistic();
        String test = "";
        for(int i = 0; i < 10; i++) {
            test += str;
        }
        List<Map.Entry<String,Integer>> list = tool.maxNum(test);
        for (int i = 0; i < list.size(); i++) {
            if (i >= 10) break;
            Map.Entry<String,Integer> entry = list.get(i);
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }

avator

  • 计算模块的测试覆盖率
    avator
    avator

2.8异常处理说明

  • 在cmd输入的参数不是两个。
if(args.length != 2) {
    System.out.print("参数个数有误");
    return;
}
  • 在进行文件操作时发生的找不到文件、打不开文件等异常,找不到输出文件时会自动创建。
try {
    ...
}
catch (IOException e) {
    System.out.println("打开文件失败");
    e.printStackTrace();
}
finally {
    ...
}

2.9心路历程与收获

说实话,一开始粗略阅读这份作业的时候,各种要求呀什么的都没有仔细看,我真的以为这只是一次简单的编程作业,等我正式地开始完成作业时,才发现这并没有我想象中的那么简单。首先是对Git和Github的学习和使用,这是一次全新的体验,Github不仅可以查看别人完成的项目,对于开学后的多人编程也着有很大的帮助。接下来就是编代码,测试,改代码,再测试,再改代码......,这一阶段是我耗时最多的地方,和一开始预计的时间也差了很多。我反思了一下,一点是因为以前对于单元测试和性能改进都没有要求,上手比较生疏,还有一点就是对于Java的一些知识没有学到位。如果要形容这次完成作业的心路历程的话,就好比过山车,一开始的平静,到中间的起伏,再到最后的平静中带有一丝成就感。这次的收获有对于Github的使用,还有就是熟悉了 分析—>设计—>编码—>测试—>改进—>总结 这种较为正式的项目流程。

posted @ 2021-03-05 17:39  Srr  阅读(131)  评论(6编辑  收藏  举报