天天看点

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

这个作业属于哪个课程 <2021春软件工程实践|S班>
这个作业要求在哪里 <作业要求>
这个作业的目标

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

2.学习github和git的使用

3.编写词频统计程序

4.学习并进行单元测试

其他参考文献

1.正则表达式的学习

2.git和github的学习

3.邹欣:关于单元测试和回归测试

4.IDEA中使用JUnit4

目录
  • 作业一 阅读《构建之法》并提问
    • 问题1:“足够好”的软件
    • 问题2:注释及可移植性
    • 问题3:代码复审
    • 问题4:创新思维
    • 问题5:提高商品竞争力
    • 附加题:软件工程发展的过程中有什么你觉得有趣的冷知识和故事?
  • 任务二 WordCount编程
    • 0.作业描述
    • 1.Github项目地址
    • 2.PSP表格
    • 3.解题思路描述
    • 4.代码规范制定链接
    • 5.设计与实现过程
    • 6.性能改进与测试
    • 7.单元测试
    • 8.异常处理说明
    • 9.心路历程与收获

在课本P15中写着:一些同学认为,所谓好软件,就是软件没有缺陷(Bug),所谓软件工程,就是把软件中的Bug都消灭掉的过程。P17中写着:“足够好”的软件不是期末前两天由两三个同学熬通宵赶出来的急就章,而是经历了一定的软件流程,通过全体团队成员的努力,在一个长期阶段内逐步完成的。

一个软件的好坏应该更主要由什么来评价,评价标准是怎样呢?通过搜集资料发现软件的好坏得到的大多是来自于用户的评价。我的想法:我觉得评价应该注重的是用户评价,看软件的受众是什么,将自己代入受众的角度去体验软件,如果舒适度便捷度等都合理,那么软件应该就挺好的。

在课本P74中写着:注释应该只用ASCII字符,不要用中文或其他特殊字符,否则会极大地影响程序的可移植性。

像在于现阶段,我对于很多注释还是采取使用中文的方式,此时的我的认知中并无法体会到这之中的影响。我想知道在之后的工作中是否每个人的注释基本都采用英语,或者是什么样的情况。再对于文中提及的可移植性。注释对于每个函数都有必要添加吗,比如一些工具类的小函数,还是应该怎么选择需要注释的地方。

在课本P79中写着:软件工程中最基本的复审手段,就是同伴复审。

当局者迷,旁观者清。我理解了同伴复审能够看到更多自己看不到的错误或者遗漏,这有利于效率的提高。但就大学里所遇到的来说,我对于别人的代码常常会遇到看不懂的情况,或者代码风格差异较大的情况,我们对于同伴的选择是否优先考虑水平接近,代码风格接近。对于以后进入公司后我想大多应该是团队任务,在那里对于代码复审的情况又是如何呢?

在课本第16章“IT行业的创新”中写着:最近几年,我们整个社会对创新都很感兴趣,媒体上充斥着创新型的人才、创新型的学校、创新型的公司、创新型的社会等名词,有些城市还把创新当做城市精神之一,还有城市要 批量生产上千名顶级创新人才。

现在国内对于创新十分重视,由“中国制造”转向“中国创造”。IT行业对于创新思维的要求相对会更高,那么在平常中该如何去提高这种思维方式。我的理解是从需求中出发进行创新,还有别的方法吗?我认为对于创新的诞生,主要还是从需求出发,因为有需求,为了满足这种需求而创造出更便捷的方式。还有就是平常生活中要多关注客观事物的不同性与特殊性。

在课本P373的魔方的创新故事中,果冻从其他地方带来魔方,这是创造了一个原本这个地方没有的事物;小飞复印了魔方口诀表来提高自己的竞争力,大牛通过改变游戏规则来先做到别人不能做到的事,但最后都是因为同学们对魔方的兴趣渐渐失去而失去了市场。

这三位同学的行为也对应了不同的选择:1.去封闭的地方卖魔方,那里的人不知道外面的世界。2.依靠自己别的优势或垄断。3.开发有差异化的新东西,体现独特的价值。对于一个事物大众化的过程中(很多人都能够实现),还有什么方法能够提高它的竞争力。

一次,冯·诺伊曼在晚会上,女主人勇敢地向他提出一个谜题:两列火车在同一轨道上以每小时 30 英里的速度相对而行,且相距 1 英里,这时栖在一列火车前面的一只苍蝇以每小时 60 英里的速度朝着另一列火车飞去。当它飞到另一列火车时,它又迅速地飞回来。它一直这样飞过去飞回来,直到两列火车不可避免地发生碰撞。问这只苍蝇共飞了多少英里?几乎在女主人刚解释完问题的同时,冯·诺伊曼就答道:“1 英里。”“太让我惊讶了,你这么快就算出来了。” 她说道。“大多数数学家都没能看出这里面的技巧,而是用无穷级数去计算,这花费了他们很长时间。”“什么技巧?我也是用无穷级数算的。” 冯·诺伊曼回答道。参考来源

这个故事有着很多版本,冯·诺伊曼从小就被寄予厚望,他也为计算机行业做了许多贡献。

在大数据环境下,搜索引擎,电商系统,服务平台,社交软件等,都会根据用户的输入来判断最近搜索最多的词语,从而分析当前热点,优化自己的服务。首先当然是统计出哪些词语被搜索的频率最高啦,请设计一个程序,能够满足一些词频统计的需求。

作业仓库:PersonalProject-Java

个人github项目地址:https://github.com/camocd/PersonalProject-Java

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

  • 刚开始看到题目的时候,对于github和git的使用是比较少的,这也给我带来了一些困扰,不过通过查找一些资料后,也渐渐会使用了,也感受到了其中的便利。
  • 编程作业,首先从题目中提取重要信息
    1. 输入、输出文件以命令行参数传入
    2. 输入文件只考虑Ascii码,不考虑汉字
    3. 空格,水平制表符,换行符,均算字符
    4. 任何包含非空白字符的行,都需要统计
    5. 只输出频率最高的10个单词,形式均为小写格式
    6. 输出时冒号后面包含一个空格
  • 对编程任务进行分解
    1. 读取input文件中的数据
    2. 统计字符数
    3. 统计单词数
    4. 统计行数
    5. 统计各单词的出现次数
    6. 将各种结果输出到output文件中
  • 在编程设计的时候考虑到的问题
    1. 应该用什么读取文件中的数据
    2. 单词可以用String类的split拆分
    3. 应用什么来判断分解出来的字符串是一个单词
    4. 该怎么判断行
    5. 单词及其频数用什么存储可以方便输出(Map类)

221801230的代码规范

只有两个类,WordCount类和Lib类,用WordCount类来执行Main函数,命令行参数传入文件名,对于传入文件后的统计和输出的处理都在Lib类里写。Lib里主要包含

readFile

writeFile

charNumCount

wordNumCount

lineNumCount

几个函数

  1. 读取文件
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里,进行统计。

  1. 在统计单词数的时候已经将单词及出现次数存在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进行排序

  1. 输出文件

    用BufferWriter类的write方法进行输出

  • 读写文件的时候对比采用BufferReader和BufferWriter类

    带有缓存的读写类能够减少操作系统访问文件的次数,加快程序的运行速度。

    输入测试input.txt

    软工实践寒假作业(2/2)
    相应输出结果ouput.txt
    软工实践寒假作业(2/2)
    运行时间
    软工实践寒假作业(2/2)

  1. 对字符计数进行测试
@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);
    }
           
  1. 对单词计数进行测试
@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);
    }
           
  1. 对行数计数进行测试
@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);
    }
           
  1. 覆盖率截图
    软工实践寒假作业(2/2)
    覆盖率未满为trycatch异常处理

主要是对于文档打开操作的异常处理

try {
    BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile));
} catch (IOException e) {
    System.out.println("文件打开失败");
    e.printStackTrace();
}
           
  1. 命令行参数数量不正确的处理
if (args.length != 2){
    System.out.println("命令行参数应该为两个");
    return;
}
           

  • 刚看到作业的时候,心情是很忐忑的,因为这次作业的内容需要通过github和git来完成,对于之前,也只是偶尔从github上下载东西,对于使用倒是一窍不通,还好通过学习,现在已经会使用一些了,也能够体会到git的方便。还有就是对单元测试的学习,基本算是会运用了。
  • 而且一段时间较少使用Java编程了,通过这次作业,也重新温习了一些运用,也加深了对Map类的运用。
  • 也逐渐地认识到了自己的代码规范,好的代码规范可便于编程,有利于编程。
  • 还有就是对于时间的规划,我对于时间的规划还存在一定的问题,对时间有规划也能够提高自己的效率,会不断学习提高自己。
  • 复习了正则表达式,这次作业也让我对于正则表达式有了更多的运用,使用起来也更加熟练