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

这个作业属于哪个课程 2021春软件工程实践|S班 (福州大学)
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 阅读《构建之法》、学习使用git以及github、WordCount编程、撰写博客
其他参考文献 CSDN、博客园、知乎......

目录

part1:阅读《构建之法》并提问

问题一

读了56~57页“专和精的关系”,我想问的是究竟该往哪个方向发展:是成为一个什么都会一点的全栈工程师,还是成为只精通某一种语言的工程师?哪个方面会更吃香?
我自己是觉得可以成为精通一门语言的工程师比较好,把这门语言学到深,真正去理解这门语言,使用的时候就会得心应手。当然,这不意味着不要去学习其他语言、技术,只是不需要学得那么深入,会基本语法,能够使用就行,自身是需要精通一门语言的,才有底气。

问题二

读了74页的注释规范,这里说“应该只用ASCILL码,不能用中文字符”我有点不大理解?
我的观点是认为注释的作用就是为了让其他程序员,甚至是自己看得懂代码写的是什么功能,是易读的。如果在欧美国家,用ASCII码当然没问题。但是在我们自己看来,英文注释反而大大阻碍了其可读性,我遇到英文注释,可能还需要借助翻译工具,这样就浪费了我的时间。而且写中文注释可以同英文代码更好的区分开来,哪个是注释,哪个是代码。

问题三

读了84~85页,我认识到结对编程的必要之处,那我想问如何进行有效的结对编程?若是其中一个人能力不足,或者总在偷懒,又因为项目要到时间了,另一个人只能被迫完成大部分内容,这样的结对编程反而无法带来好处,那么如何避免这种情况的出现呢?
我自己的能想到的解决方法是,找个能力相近的有责任心的人一起结对编程。不知道还有没有更好的解决方法?

问题四

敏捷开发的一个原则是“可用的软件是衡量项目进展的主要指标”,我想问的是“可用的软件”的具体含义指的是什么?指的是仅实现功能需求的软件,但是仍存在一些bug,还是指的是无bug的软件?如何判断一个软件是可用的?有没有什么具体的标准?
我的认知可用的软件是指实现功能需求且目前暂未发现bug的软件,有bug的软件虽然可能还是可用的,但是bug迟早是要解决的,我更愿意把无bug的软件称之为“可用的软件”。

问题五

读到第八章收集用户需求时,我有一个问题:软件开发时是满足大部分人所需要的需求,还是尽量满足所有用户各式各样的需求,即对小众用户的需求是否要满足?比如手机的旁白模式,这个对大多数人是用不到的,但市面上基本所有的手机都实现了这个功能,因为他对一些人来说这是非常必要的。
因此,我的观点是尽量满足用户的各种合理的需求,这无疑会增加编程的工作量,但是,这样的软件才能更好的服务用户。

Android并不是Google的亲儿子

在2003年,AndyRubin,Rich Miner,Nick Sears,和 Chris White 四人共同研发(Android就是用AndyRubin的昵称来命名)直到2005年Android被Google收购,从而改变了这个手机操作系统的命运。所以Android并不是Google自行开发的产品,而是收购得来的。
参考链接

part2:WordCount编程

Github项目地址

PSP表格

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

解题思路描述

一开始看到题目时,发现是在命令行运行程序,有点懵。查找资料后才明白args就是命令行传入的参数,这么一来就知道文件的路径,那么问题就变得好解决了。
我的解题步骤如下:
1、读入文件并转为字符串,于是直接百度Java读取文件的操作,最后选择了一个字符一个字符的读文件。
2、统计总字符数:这个就很简单了,字符串的长度就是总字符数。
3、统计单词总数:在查找资料之后,发现正则表达式是个很不错的选择,于是花时间学习了一下正则表达式的用法。
4、统计有效行数:我选择的方法是一行一行的重新读文件,遍历每一行的字符,若是遇到不是空白字符的字符,说明这一行是有效行,行数+1。
5、统计单词频数:map无疑是个很好的选择,可以存储key、value,其本身还自带根据key、value进行排序的方法。
最后输出结果到文件则是查阅Java合适、且同时可把编码格式设为UTF-8的输出方法。

代码规范制定链接

设计与实现过程

程序一共有两个类:WordCount类:用于读取命令行的参数,并根据文件路径构造File,只有main函数
HandleTxt类:用于处理File的类,其中有Changestr方法:将文件内容转为String字符串;Returnnum方法:返回文件总字符数;Getwords方法:返回单词总数;Getlines方法:获取文章的有效行数;GetTen方法:排序,并取前十个频率最高的单词的Map;Ouputxt方法:输出结果至output.txt。
在WordCount创建一个HandleTxt的对象,在HandleTxt的构造函数中调用处理文件的方法。

Changestr实现

选择一个字符一个字符地读取文件,同时将得到的字符串转为小写。其中发现了Windows回车是\r\n,是两个字符。

关键代码

        StringBuffer sb=new StringBuffer();
        FileInputStream fileInputStream=new FileInputStream(in);
        while ((n=fileInputStream.read())!=-1)
        {
            char ch=(char)n;
            sb.append(ch);
        }
        txt=sb.toString();
        txt=txt.toLowerCase();

Returnnum实现

由于在Changestr方法中得到了文件内容的字符串,因此只要获取字符串长度就得到了文件总字符数

关键代码

public int Returnnum()
    {
        return txt.length();
    }

Getwords实现

返回单词总数选择用正则表达式进行匹配。

关键代码

Pattern pattern=Pattern.compile("(^|[^a-z0-9])([a-z]{4}[a-z0-9]*)");
        Matcher matcher=pattern.matcher(txt);
        while (matcher.find())
        {
            words++;
        }

Getlines实现

选择重新一行一行读文件,若遇到行中存在为非空白字符的字符,行数加一

关键代码

String content=br.readLine();
        StringBuilder sb=new StringBuilder();

        while (content!=null)
        {
            for(int i=0;i<content.length();i++)
            {
                if(content.charAt(i)!='\t'&&content.charAt(i)!='\n'&&content.charAt(i)!=' '
                        &&content.charAt(i)!='\r')
                {
                    line++;
                    break;
                }
            }
            content=br.readLine();
        }

GetTen实现

同样用正则表达式进行匹配,结果选择使用Map进行存储和排序,并取排序后的前10个

关键代码

while (matcher.find())
        {
            if (Legalchar(txt.charAt(matcher.start()-1)))
            {
                word=matcher.group();
                if (WordsMap.containsKey(word))//Map中已含有此单词
                {
                    WordsMap.put(word,WordsMap.get(word)+1);
                }
                else
                {
                    WordsMap.put(word,1);
                }
            }
        }
        //排序结果取前十个存放到result中
        WordsMap.entrySet().stream().sorted(Map.Entry.<String, Integer> comparingByValue().reversed()
                .thenComparing(Map.Entry.comparingByKey())).limit(10)
                .forEachOrdered(x->result.put(x.getKey(),x.getValue()));

Ouputxt实现

使用PrintWriter,设置编码为UTF-8,将字符串输出到b.txt

关键代码

PrintWriter pw=new PrintWriter(new OutputStreamWriter(new FileOutputStream(out),
                "UTF-8"));

        Iterator<Map.Entry<String, Integer>> it=GetTen().entrySet().iterator();
        sb.append("characters: "+Returnnum()+"\n"+"words: "+Getwords()+'\n'
                +"lines: "+Getlines()+'\n');
        while(it.hasNext())
        {
            Map.Entry<String, Integer> entry=it.next();
            sb.append(entry.getKey()+": "+entry.getValue()+"\n");
        }
        pw.print(sb.toString());
        pw.flush();
        pw.close();

性能改进

运行3,251,088 字节的文件,耗时7309ms。

改进方法:在获取行数的方法中,一旦读到非空白字符,直接break,无需遍历整行。

if(content.charAt(i)!='\t'&&content.charAt(i)!='\n'&&content.charAt(i)!=' '
                        &&content.charAt(i)!='\r')
                {
                    line++;
                    break;
                }

单元测试

测试returnnum方法

测试能否正确读入\n\t@$等字符,字符数是否有错。

String str="dskjsh kf"+"\n\t\r"+"fdk233458884*@@$$^QJ BCJKL";
        HandleTxt lib=new HandleTxt(str);
        assertEquals(lib.Returnnum(),str.length());

测试getwords方法

测试其能否正确判断单词,其中只有"assd "、"\nfile123"为单词。

String str="assd "+"*1sded34"+" ws123"+"\nfile123";
        HandleTxt lib=new HandleTxt(str);
        assertEquals(lib.Getwords(),2);

测试getTen

测试函数能否正确存储单词,并排序。

String str=" asdd23  ASdd23 aSdd23 dfgrrrr ghjth cxccxd *zsdsds zsdsds zsdsds zsDsds\n"+"         ddfg123 fgfdg234\n"+"\n" +
                "          Sfgh\n" +
                "\n" +
                "dsdaasdas asdd\n";
        str=str.toLowerCase();
        HandleTxt lib=new HandleTxt(str);
        Map<String,Integer> map=new HashMap<>();
        map.put("zsdsds",4);
        map.put("asdd23",3);
        map.put("asdd",1);
        map.put("cxccxd",1);
        map.put("ddfg123",1);
        map.put("dfgrrrr",1);
        map.put("dsdaasdas",1);
        map.put("fgfdg234",1);
        map.put("ghjth",1);
        map.put("sfgh",1);
        assertEquals(lib.GetTen(),map);

测试覆盖率截图

可以使用读入文件的方式,去测试Changestr、Getlines方法,从而优化覆盖率。

异常处理说明

只对WordCount读入的参数做了异常处理,其余部分选择直接throws Exception。

if(args.length==0)
        {
            System.out.println("没有参数");
        }
        else if(args.length==1)
        {
            System.out.println("输入参数只有一个");
        }
        else if(args.length==2)
        {
            File in=new File(args[0]);
            File out=new File(args[1]);

            if(!in.exists())
            {
                System.out.println("输入文件不存在");
            }
            else if(!out.exists())
            {
                System.out.println("输出文件不存在");
            }

心路历程与收获

心路历程

  • 一开始看过去觉得这个题目不难,之前写过类似的项目。直到做了才发现,它是从命令行读取参数,从哪里获取参数一开始就难到我了。之后的读取文件同样也是个考验,一开始我是选用readline读取文件,可是无法读入\n,于是我选择每读一行添加一个\n,这样导致的结果是最后一行必定会有一个\n,不符合要求。然后我选择了一个字符一个字符的读取,结果被Windows的回车埋伏了一手,因为Windows的回车是两个字符,导致我一直以为代码哪里出了问题。
  • 同时还学习了新工具git、github的使用,从一开始的拒绝到最后发现这真是个好工具。
  • 同时还认识到大任务其实不可怕,只要拆分成一个个的小任务,认真完成每一个小任务,当最后一个小任务完成时,大任务也便完成了。这次作业付出了许多精力和时间,但同样的收获也是满满的。

收获

  • 学会了使用git、github
    一开始看到要用git、github,我的内心是拒绝的。因为我从未使用过这两个工具,怕自己学不会。当看完教程,学会使用,并且在项目中使用后,才发现其功能的强大。能够有效的控制版本,进行代码管理,代码的修改一目了然,做了什么改动也有commit信息,我已经爱上了这两个工具了。
  • 学会了单元测试
    学会使用Junit进行测试,发现这个真方便,IDEA还能快捷键生成测试类。通过Junit,我就可以单独方便地对我写的某一个函数进行测试,排除bug。不需要再像往常一样需要运行整个程序,使我的工作完成得更轻松,大大减少我花在调试上面的时间。
posted @ 2021-02-22 10:55  羊羊羽  阅读(117)  评论(2编辑  收藏  举报