1 2 3 4

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


基本信息

| 这个作业属于哪个课程 |2021春软件工程实践S班(福州大学)|
---|:---|:---:
| 这个作业要求在哪里 | 软工实践寒假作业(2/2) |
| 这个作业的目标| 1、重读阅读之法提出问题
2、熟悉git和Guihub Desktop的使用
3、编写字符统计程序
4、掌握单元测试
5、掌握性能测试|
|作业正文| 作业正文 |
| 其他参考文献 | 《构建之法》
github简单教程
github fork 与pull request
关于单元测试和回归测试
《码出高效_阿里巴巴java开发手册》|

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

一、《构建之法》

1.如何避免过早优化?

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

思考与查证:一开始刚看到过早优化的这四个字的时候,脑子想的是优化难道不是越早考虑越好吗,当看到书中的例子以及网上查阅的资料才知道是自己想得过于简单了。过早设计有两个结果:一些设计根本不可能用上,另一些等到用上的时候,发现当初设计的时候考虑不全,还不能用,正所谓半吊子。所以为避免过早优化我们每次优化前可以先问自己三个问题:1,优化之后,代码量会减少吗?2,优化的地方是系统瓶颈吗?3,你的优化方案,是否依赖于某些隐藏条件,或是为系统增加了限制?如果可以满足这三个条件,就大胆去优化吧!

2.在实际开发中要如何权衡的软件质量成本?

p314 原文:要达到一定的软件质量,是要付出成本的。SWEBOK特别定义了软件质量成本的组成部分,其中包括预防、评审、内部故障、外部故障这四个方面,作者认为还要加上流程分析改进、投资改进等各种成本

思考和查证:菲根堡姆和朱兰《全面质量管理》书中指出:产品质量必须始终同成本联系在一起.离开"成本"去谈"质量"是毫无意义的.实行全面质量管理的过程中,来分析 研究质量成本,有利于控制和降低成本,有利于满足用户质量成本方面的要求;促进领导重视产品质量,支持质量管理工作,有利于操算最适宜的质量水平.我认为就像书中所说磨刀不误砍柴工,想砍柴不磨刀是绝无可能的,我们能做的就是努力去提高自己学习新事物的能力和提高自身职业技能来减少“磨刀”时间。

3.要不要使用goto?

p75 原文:函数最好有单一的出口,为了达到这一目的,可以使用goto。只要有助于程序逻辑的清晰体现,什么方法都可以使用,包括goto

思考和查证:我的观点是反对的,从一开始接触到goto老师就反复强调这个语句最好不要使用,goto语句会更改程序执行的正常顺序,使程序逻辑变得非常复杂,不易阅读而且使用goto语句使得分析和验证程序尤其是涉及循环的程序的正确性的任务非常困难。在1968年E·W·代克斯特拉甚至提出了“GOTO语句是有害的”论点。

4.当测试人员与开发人员产生冲突时,如何让他们摒弃前嫌更好的协作呢?

p147 原文:
问:那有冲突怎么办?
答:那就吵呗。各个角色的利益是有一定冲突的,MSF 没有掩饰这一点。MSF 团队模型的核心是,成功的技术项目必须符合各种利益相关人(Stake holder)完全不同且常常对立的质量观点。
问:这么说在团队中有矛盾是正常的了?
答:对!例如,用户代表觉得新增加一个功能很酷,因为新功能“让产品更好用”,但是项目经理角色觉得会影响“按约束条件内交付产品”的目标,测试会觉得“保证所有问题都得到处理”的目标受到威胁,用大白话说,就是“我没有时间测试你的新功能,因此不能加这个功能”。这就要各方在整个项目的共同利益之下,协商解决,寻求多赢。

思考与查证:在一个团队中测试人员和开发人员之间的沟通不畅容易进一步影响程序的发布日期而当开发人员和测试人员合作时,他们能够更好地进行交流。正确的沟通有助于确保更好地了解两个团队之间的需求,从而加快项目交付速度。查阅资料后有以下几个方法:1.测试人员从早期开始测试,左移测试!2.随时了解彼此的活动3.确保测试团队参与代码审查4. QAOps:持续测试的关键5.自动化的单元测试。

5.当有两个团队邀请你,一个是较好的团队但工作风格与自己差异较大,一个一般团队但工作风格相似,应如何选择?

p97 原文:软件团队有各种形式,适用于不同的人员和需求。基于直觉形成的团队模式未必是最合适的。

思考:我的想法是如果选择了较好的团队由于工作风格的差异可能自己需要花费大量的时间去适应他们的工作风格,然而工作风格并不是短时间就能形成的,这个过程必定是漫长的。若是选择一般的团队,大家工作作风相似可以互相帮忙共同鼓励互勉,可能一开始大家没那么优秀,但成长的这个过程必定是愉悦且充实的。毕竟现实还没遇到这种情况,以上只是我不成熟的想法。

二、附加题

有这样一个笑话:一个旅客走进硅谷的一家宠物店,浏览展示的宠物。这时,走进一个顾客,对店主说:"我要买一只C猴。"
店主点了点头,走到商店一头的兽笼边,抓出一只猴,递给顾客说:"总共5000美元。"顾客付完款,然后带走了他的猴子。
另一位旅客非常惊讶,走到店主跟前说:“那只猴子也太贵了!"
店主说:"那只猴子能用C编程,非常快,代码紧凑高效,所以值那么多钱。"
这时,那位旅客看到了笼子中的另一只猴子,它标价10000美元。于是又问:"那只更贵了!它能做什么?"
店主回答:"哦,那是一只C++猴;它会面向对象的编程,会用Visual C++,还懂得一点Java,是非常有用的。"
旅客又逛了一会儿,发现了第三只猴子,它独占一个笼子,脖子上的标价是50000美元。旅客倒抽一口气,问道:"那只猴子比其他所有猴子加起来都贵!它究竞能做什么?"
店主说:"我们也不知道它究竟能做什么,不过它是做项目顾问出身的。
https://bbs.csdn.net/topics/10123801

思考:这虽然是个小笑话,但是其中蕴含着许多现况。项目管理在当今社会中占有重要的地位。一个项目的成功和与否,关键一点就是,看项目管理是否得当。所以,项目管理是项目成功和的核心部分,是项目的灵魂,然而项目管理人才一直是“稀缺物种”,一个优秀的软件公司必定拥有优秀的项目管理人才

part2:WordCount编程

1.Github项目地址:

Github项目地址

2.PSP表格

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

3.解题思路描述

刚拿到题目要求时,通读了一遍全文,发现要求编写的几个功能函数都是相对基础的,但是也遇到了几个新事物:1、git和GitHub 2、性能测试 3、单元测试
于是制定了以下几个步骤:
1、通过查看博客和视频资料学习git和GitHub的使用
2、通过Github Desktop编写代码并commit
3、学习如何测试性能并改进部分函数的性能
4、学习单元测试的概念和使用方法,测试代码
5、编写博客,完成作业

4.代码规范

代码规范

5.模块接口的设计与实现过程。

整个程序包括两个类分别是:WordCount.java和Lib.java
WordCount.java中的函数:

  • WordCount
  • main
  • readFile
  • writeFile

Lib.java中的函数:

  • countChars
  • countLines
  • countWords
  • getSortedList

关键函数关系:

readFile通过读取文件得到一个字符串并赋值到countChars、countLines、countWords三个函数中,这三个函数返回字符数、行数、单词数到writeFile.
countWords返回单词数的同时也为map赋值,getSortedList通过赋值过的map得到一个有序的列表并返回到writeFile.

关键代码:

1、统计字符数
读取文件所获得的字符串长度便是字符数。

public static int countChars(String text) {
    return text.length();
}

2、统计行数
通过使用split()将换行符指定为边界将字符串分割成字符串数组,并将空白删除。

public static int countLines(String text) {
    int count = 0;
    String[] textArrays = text.split("\n|\r\n");
    for (String validLine : textArrays) {
        if (!validLine.replaceAll("\r|\n", "").trim().equals("")) {
            count++;
        }
    }
    return count;
}

3、对map进行排序
通过将map转化为List并通过根据指定比较器产生的顺序对指定列表进行排序。

public static List<HashMap.Entry<String, Integer>> getSortedList(Map<String, Integer> map) {
    List<Map.Entry<String,Integer>> lstEntry = new ArrayList<>(map.entrySet());
    List<Map.Entry<String,Integer>> list = new ArrayList<>();
    int count;
    Collections.sort(lstEntry,((o1, o2) -> {
        if (o1.getValue().equals(o2.getValue()))
            return o1.getKey().compareTo(o2.getKey());
        else
            return o2.getValue().compareTo(o1.getValue());
    }));
    if (lstEntry.size() < 10) {
        count = lstEntry.size();
    } else {
        count = 10;
    }
    for (int i = 0; i < count; i++)
	list.add(lstEntry.get(i));
    return list;
}          

4.调用Lib中的函数进行赋值、初始化

public WordCount(String fileName) throws IOException {
    map = new HashMap<String, Integer>();
    text = readFile(fileName);
    countChars = Lib.countChars(text);
    countWords = Lib.countWords(text, map);
    countLines = Lib.countLines(text);
    lstEntry = new ArrayList<>(map.entrySet());
    lstEntry=Lib.getSortedList(map);
}

6.性能改进

改进前代码:

public static int countLines(String text) {
    int count = 0;
    String[] textArrays = text.split("\n|\r\n");
    for (String validLine : textArrays) {
        if (!validLine.replaceAll("\r|\n", "").trim().equals("")) {
            count++;
        }
    }
    return count;
}

改进前性能测试图:

avater

通过性能分析图我们可以看到countLines中的replaceAll占了一大部分时间,所以要进行性能改进可以从这个方面下手,通过思考和测试后发现由于split()函数已经不存在换行符了,这时replaceAll函数是多余的,应将其删除,提高性能。

改进后代码:

public static int countLines(String text) {
    int count = 0;
    String[] textArrays = text.split("\n|\r\n");
    for (String validLine : textArrays) {
        if (!validLine.replaceAll("\r|\n", "").trim().equals("")) {
            count++;
        }
    }
    return count;
}

改进后性能测试图:

avater

7.单元测试展示

1、统计字符测试
测试函数中包含了空格,水平制表符,换行符出现的情况

public class CountCharTest {

    public static void main(String[] args){
        String string = "he l\nl\t";
        String text = "";
        for(int i = 0; i < 500; i++){
            text += string;
        }
        if (countChar.countChar(text) == 3500)
            System.out.println("pass");
    }

}

2、统计行数测试
测试函数中测试了出现空白行情况

public class CountLineTest {

    public static void main(String[] args){
        String text = "ab\n\n\nfdg3\n\n";
        if (countLine.countLines(text) == 2)
            System.out.println("pass");
    }

}

3、统计单词数测试
测试函数中测试了至少以4个英文字母开头,跟上字母数字符号、不以字母开头以及开头字母不足4个的情况。

public class CountWordTest {

    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();
        String string = "hello,123kkk,kkk*";
        String text = "";
        for (int i = 0; i < 500; i++) {
            text += string;
        }
        if (countWord.countWords(text, map) == 500)
            System.out.println("pass");
    }

}

4、获得频率最高的十个单词测试
测试函数测试了两种情况分别为频率最高先输出、频率相同先输出字典序靠前的单词。

public class GetSortedListTest {

    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();
        String string = "file1,file1,file2,file3,file4,file5,file6,file7,file8,file9,filea,fileb,";
        String text = "";
        for (int i = 0; i < 500; i++) {
            text += string;
        }
        countWord.countWords(text,map);
        List<Map.Entry<String,Integer>> lstEntry = new ArrayList<>(map.entrySet());
        lstEntry=getSortedList.getSortedList(map);
        Iterator<Map.Entry<String,Integer>> iter = lstEntry.iterator();
        int num = 0;
        while (iter.hasNext()) {
            Map.Entry<String,Integer> m = (Map.Entry<String,Integer>) iter.next();
            System.out.println(m.getKey()+":"+m.getValue());
            num ++;
            if (num == 10)
                break;
        }
    }

}

测试结果:

file1:1000
file2:500
file3:500
file4:500
file5:500
file6:500
file7:500
file8:500
file9:500
filea:500

覆盖率:
varter

8.异常处理说明

1、输入参数不足两个抛出IO异常

public static void main(String[] args) throws IOException {
    if (args.length != 2)
        System.out.println("wrong");
    return;
}

2、文件不存在时抛出IO异常

public static String readFile(String path) throws IOException {
    BufferedReader br = null;
    StringBuffer textBuffer = new StringBuffer();
    String content = null;
    int s;
    try {
        br = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
        while ((s = br.read()) != -1)         
            textBuffer.append((char)s);            
        content = textBuffer.toString();
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         br.close();
     }
     return content;
}

9.心路历程与收获

一开始看到作业的要求心情是非常复杂的,因为出现了许多之前没有接触过的新事物,当看到编写代码的要求时,发现所要实现的代码功能较为基础,所以迫不及待的想要开始编写代码。但是,作业要求需要接触新的工具GitHub于是上网开始查阅资料,由于资料的复杂和自己激进的心态使得自己十分浮躁。而在当我阅读《构建之法》第十四章质量保障时,我豁然开朗。书中举了一个例子移山公司的工程师只有20%的时间来写新功能的代码,其他时间都消耗在了提高职业技能、学习新的技术等。“磨刀不误砍柴工”,只有不断使自己的“刀”更加锋利,才能做一个更优秀的“砍柴工”!

posted @ 2021-03-04 17:56  梁扬新  阅读(215)  评论(4编辑  收藏  举报