软件工程实践第二次作业——个人实战

Deee- 学生 2023-03-03 21:13:24
这个作业属于哪个课程2023年福大-软件工程实践-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标1、完成对澳大利亚网球公开赛相关数据的收集
2、实现一个能够对赛事数据进行统计的控制台程序
3、进行性能分析和改进
4、使用单元测试对项目进行测试
5、撰写博客
其他参考文献具体参考教程在相应内容里注明

目录

  • 一、Gitcode项目地址
  • 二、PSP表格
  • 三、解题思路描述
  • 3.1、爬取数据
  • 3.2、解析数据
  • 3.3、读取input.txt文件和写入out.txt文件
  • 3.4、单元测试
  • 3.5、性能优化
  • 3.6 、打包Jar包
  • 四、接口设计和实现过程
  • 4.1 、代码组织
  • 4.1.1 、主要的类
  • 4.1.2 、主要的函数
  • 4.1.3 、文件写入的流程图
  • 4.2 、算法的关键
  • 4.3 、独到之处
  • 五、关键代码展示
  • 5.1 、两个序列化的类Players和Schedule(解析数据)
  • 5.2 、input.txt文件的读取后指令的处理
  • 5.3 、output.txt文件的写入
  • 六、性能改进
  • 七、单元测试
  • 7.1 、部分测试函数
  • 7.2 、测试覆盖率情况
  • 八、异常处理
  • 8.1 、命令行参数输入错误
  • 8.2 、文件不存在
  • 九、心得体会

一、Gitcode项目地址

我的Gitcode项目地址

二、PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2520
• Estimate• 估计这个任务需要多少时间2015
Development开发800720
• Analysis• 需求分析 (包括学习新技术)150160
• Design Spec• 生成设计文档4030
• Design Review• 设计复审2020
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)3040
• Design• 具体设计3040
• Coding• 具体编码300400
• Code Review• 代码复审3045
• Test• 测试(自我测试,修改代码,提交修改)200350
Reporting报告6050
• Test Repor• 测试报告3035
• Size Measurement• 计算工作量2025
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划2030
合计17751980

三、解题思路描述

3.1、爬取数据

注:该爬取行为只适用于课程教学!

思路:得知数据不需要在程序中爬取(联网),只需要在官网的api中获取后,在网上找到了如何通过浏览器抓包获取API的教程,找到对应数据文件,再通过转换工具将数据格式化。
参考教程使用浏览器抓包获取API

具体步骤
(1)打开我们要爬取数据的网站澳大利亚网球公开赛官网,进入赛程的界面。

(2)同时按Fn和F12,打开开发人员工具

(3)选择开发人员工具的网络一栏并刷新页面,找到一个名为result的文件就是我们要找的数据(这里以获取Q1数据为例)

(4)双击该文件,即可获得我们需要的源数据
附:Q1的API
(5)复制API中的数据,通过在线Json工具完成数据的格式化

3.2、解析数据

思路:采用Gson解析Json数据,新建两个类分别对应选手信息和赛程信息,每个类只存需要的属性,利用Gson进行反序列化,最终通过两个类中的属性获取数据。
参考教程:(1)使用Gson解析复杂的json数据 (2)Java使用GSON对JSON进行解析

具体步骤
(1)首先下载Gson的jar包,将jar包导入IDEA
参考教程:IDEA引入jar包方式
(2)定义两个序列化的类Players和Schedule(采用内部类的形式),存放需要的属性
(3)在Lib类中编写解析数据的方法:

  • parsePlayers:反序列化后返回的是选手信息的List,通过属性获取数据
  • parseSchedule:反序列化后返回的是Schedule类,通过属性获取数据

3.3、读取input.txt文件和写入out.txt文件

思路:
读取文件: 一行一行读取文件中的内容,如果为空行则继续读下一条指令,每行不为空的作为参数传递到一个处理指令的方法,处理完成后添加到指令列表中
写入文件:从指令列表中取指令,对指令的形式进行判断,最终输出到output.txt文件

3.4、单元测试

具体步骤:
(1)引入IDEA的Junit插件进行单元测试分析
(2)编写测试函数和测试用例进行测试

3.5、性能优化

思路: 先查看代码,看看哪里有可以优化的地方,优化完成后,对未优化和优化完成后的程序进行对比,查看优化效果。

3.6 、打包Jar包

具体步骤:
(1)点击菜单栏文件-->项目结构

(2)选择工件一栏,点+按钮,选择JAR-->来自具有依赖项的模块.....

(3)主类选择程序入口类,点击确定

(4)回到主界面,点击菜单栏构建-->构建工件...,弹出对话框,选中刚刚生成的jar,点击构建

(5)将jar包放到与src同级目录下,即可在控制台运行

四、接口设计和实现过程

4.1 、代码组织

4.1.1 、主要的类

Players类:用来存放输出选手信息所需的属性
Schedule类:用来存放输出赛程信息所需的属性

4.1.2 、主要的函数

getInput:对input.txt里的内容进行读取,每行非空的指令作为getInstruction方法的参数传入,通过getInstruction方法进行处理,加入字符串列表
getInstruction:对input.txt里的指令进行处理,返回处理后的字符串
parsePlayers:解析选手信息
parseSchedule:解析赛程信息
getOutput:将解析后的数据写入文件

4.1.3 、文件写入的流程图

img

4.2 、算法的关键

关键点1:对json文件的解析要正确,采用第三方库Gson的方法
关键点2:input.txt文件的读取要正确,编写了两个函数getInput和getInstruction进行指令的读取和处理

4.3 、独到之处

采用了第三方库Gson对json文件进行解析,利用Gson进行反序列化,最终通过两个类中的属性获取数据

五、关键代码展示

5.1 、两个序列化的类Players和Schedule(解析数据)

//选手类
public class Players {
    public List<Player> players; //选手列表

    public class Player {
        public String full_name; //全名
        public String gender; //性别
        public Nationality nationality;

        public class Nationality {
            public String name; //国籍
        }
    }
}

//赛程类
public class Schedule {
    public List<Matches> matches; //赛程列表
    public List<Teams> teams; //球员所属队的列表
    public List<Players> players; //选手列表

    public class Matches {
        public String actual_start_time; //比赛实际开始时间
        public List<Teams> teams; //对局列表
        public Match_status match_status;

        public class Teams {
            public String team_id; //标识一个team的id,为了与另外一个Teams列表关联
            public List<Score> score; //比分列表
            public String status; //标识是否为胜利者

            public class Score {
                public String game; //比分
            }
        }

        public class Match_status {
            public String code; //“C”为正常完成比赛,“W”为弃赛,“R”为退役
        }
    }

    public class Teams {
        public String uuid; //标识一个team的id
        public List<String> players; //球员uuid的列表
    }

    public class Players {
        public String uuid;
        public String short_name; //姓名缩写
    }
}

5.2 、input.txt文件的读取后指令的处理

//对input.txt里的指令进行处理,返回处理后的字符串
    public static String getInstruction(String instruction) {
        String regex="\\s+"; //删去头尾空格
        String i[] = instruction.trim().split(regex);
        if (i[0].equals("players")) {
            if (i.length == 1)
                return "players";
            else
                return "Error";
        }
        else if (i[0].equals("result")) {
            if (i.length == 2) {
                String d = i[1];
                if (d.equals("Q1"))
                    return "0109";
                if (d.equals("Q2"))
                    return "0110";
                if (d.equals("Q3"))
                    return "0111";
                if (d.equals("Q4"))
                    return "0112";
                if(isDate(i[1]))
                    return d;
                return "N/A";
            }
            else
                return "N/A";
        }
        else
            return "Error";
    }

5.3 、output.txt文件的写入

未优化之前:

//将解析后的数据写入文件
public static void getOutput(List<String> input,String path) throws IOException {
        File file = new File(path);
        if (!file.exists())
            file.createNewFile(); //如果文件不存在,则新建文件
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path) , "UTF-8"));
        for (String i:input) {
            if(i.equals("Error")) {
                writer.write("Error" + "\n" + "-----" + "\n");
            }
            else if (i.equals("N/A")) {
                writer.write("N/A" + "\n" + "-----" + "\n");
            }
            else if (i.equals("players")) {
                writer.write(parsePlayers());
            }
            else {
                writer.write(parseSchedule(i));
            }
        }
        writer.close();     
}

优化之后:

//将解析后的数据写入文件
 public static void getOutput(List<String> input,String path) throws IOException {
        File file = new File(path);
        if (!file.exists())
            file.createNewFile(); //如果文件不存在,则新建文件
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path) , "UTF-8"));
        boolean pflag = false; //标记之前是否输出过players
        boolean[] sflag = new boolean[25]; //标记之前是否输出过每个日期的赛程
        for (int i = 0; i < 25; i++) {
            sflag[i] = false;
        }
        for (String i:input) {
            if(i.equals("Error")) {
                writer.write("Error" + "\n" + "-----" + "\n");
            }
            else if (i.equals("N/A")) {
                writer.write("N/A" + "\n" + "-----" + "\n");
            }
            else if (i.equals("players")) {
                if (!pflag) { //如果从未输出过players
                    String s = parsePlayers();
                    memory.add(0 , s);
                    pflag = true;
                }
                writer.write(memory.get(0)); //之前输出过players,则直接从列表中取数据
            }
            else {
                int date = Integer.valueOf(i);
                date = date % 109 + 1;
                if (!sflag[date]) { //如果从未输出过players
                    String s = parseSchedule(i);
                    memory.add(date , s);
                    sflag[date] = true;
                }
                writer.write(memory.get(date));
            }
        }
        writer.close();
    }

六、性能改进

思路:每次要输出选手信息和赛程信息时,都要到相应json文件中取数据,想到可以把已经输出的数据放到一个列表中,并用boolean变量来判断是否之前读取过,这样一来就不用频繁去读取json文件,节省了时间
利用JProfiler对比程序运行结果(运行20万行正确指令的情况下)
未优化前:

优化后:

七、单元测试

7.1 、部分测试函数

getInstruction的测试函数

public void testGetInstruction() {
        Lib lib = new Lib();
        lib.getInstruction("result sss");
        lib.getInstruction("result0116");
        lib.getInstruction("player");
        lib.getInstruction("test");
        lib.getInstruction("result Q1");
        lib.getInstruction("result Q5");
        lib.getInstruction("result 1312");
        lib.getInstruction("result 0129");
        lib.getInstruction("result");
        lib.getInstruction("players");
 }

getOutput的测试函数

public void testGetOutput() throws IOException {
        Lib lib1 = new Lib();
        Lib lib2 = new Lib();
        lib2.getOutput(lib1.getInput("Test/abc.txt") , "Test/abcd.txt");
}

7.2 、测试覆盖率情况

八、异常处理

8.1 、命令行参数输入错误

public static void main(String[] args) {
      if (args.length != 2) {
              System.out.println("命令行应输入两个参数");
              System.exit(-1);
          }
}

8.2 、文件不存在

File file = new File(path);
        if (!file.exists()) {
            System.out.println("文件不存在!");
            System.exit(0);
        }

九、心得体会

(1)在这次作业中,给我最大的一个感悟就是要合理安排时间。如果没有及时开始实践,在ddl之前会非常手忙脚乱。应该要留足够的时间来修改bug、测试、性能优化,同时每完成一些功能后及时提交,还可以一边实现功能一边写博客,这样可以及时将当时的思路记录下来
(2)在开始写作业之前认真阅读作业要求十分重要!一开始的时候我只是粗略阅读一下作业要求就开始,这里写一写那里写一下,最后发现思维非常混乱,又重新读了一遍作业要求的博客后思路更加清晰,明确地知道要完成什么功能了
(3)在这次作业中,让我有了对程序进行性能优化的意识。以前写完一个程序能运行和正确输出后就直接提交了,不会再看那些地方可以优化,这次优化后前后程序的对比让我意识到性能优化的重要性,如果测试的数据很多话,优化的重要性就更能体现出来了
(4)最后,多多与其他同学交流也很重要。对于写程序过程中出现的问题,首先要自己动手调试,不能一遇到问题就找别人解决,如果遇到一直解决不了的问题可以与同学讨论,可能可以帮助自己打开思路,不然会一直卡在一个点上,无法推进

...全文
289 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

685

社区成员

发帖
与我相关
我的任务
社区描述
2023年福州大学软件工程实践课程W班的教学社区
软件工程团队开发软件构建 高校 福建省·福州市
社区管理员
  • FZU_SE_teacherW
  • aboutazhang
  • 郭渊伟
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧