软件工程实践第二次作业---文件读取

这个作业属于哪个课程<软件工程-23年春季学期>
这个作业要求在哪里<软件工程实践第二次作业—文件读取>
这个作业的目标<完成对澳大利亚网球公开赛相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序>
其他参考文献《构建之法》《源代码管理》

目录:

0.Gitcode项目地址

  1. PSP表格
  2. 解题思路描述
    1. 从相关网址获取json
    2. json解析
    3. 数据提取分析
  3. 接口设计和实现过程
    1. 接口设计
    2. 接口实现
  4. 关键代码展示
  5. 性能改进
    1. 分析
    2. 改进
  6. 单元测试
  7. 异常处理
  8. 心得体会

0.Gitcode项目地址

仓库地址

1. PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1010
• Estimate• 估计这个任务需要多少时间1010
Development开发600500
• Analysis• 需求分析(包括学习新技术)120100
• Design Spec• 生成设计文档2050
• Design Review• 设计复审6060
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)2030
• Design• 具体设计12060
• Coding• 具体编码360290
• Code Review• 代码复审12090
• Test• 测试(自我测试,修改代码,提交修改)120120
Reporting报告210165
• Test Repor• 测试报告8060
• Size Measurement• 计算工作量6060
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划6060
合计19701665

2. 解题思路描述

2. 1 从相关网址获取json

​ 1、在这里插入图片描述

​ 2、在这里插入图片描述

​ 3、在这里插入图片描述

​ 4、在这里插入图片描述

2.2 json数据解析

在这里插入图片描述

用vs打开压缩过的json数据,然后点击格式化

players.json的数据可以看成上面这几个部分,而我们主要需要提取的是players这一项

点开players这一项

查看

发现
需要的数据都在里面

构成json-》players-》full_name / nationality->name /gender的树形结构,代码即可从这里入手

接着分析result date的json数据

总体分为

这样的一个结构

我们需要的数据在

点开matchs

看到我们需要的数据[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.3、数据提取分析

根据需求我们需要获得winner的名字,但是根据json文件我们只能获得team_id

如图

所以需要查看该文件内teams数组内的内容

如图

再由玩家hash值去该文件的players去查找

如图

代码编写也是按照如上思路去进行

3. 接口设计和实现过程

3.1接口设计

由需求可知需要将打印全部玩家与打印全部比赛结果提取出来,所以考虑使用java8的新属性,可在接口中使用静态方法,继承者可以直接调用接口中的静态方法

  • 第一步需要实现一个静态的writePlayers的方法,内部用阿里巴巴的fastjson去解析json数据,并且根据题目要求打印
  • 第二部需要实现静态的writeResults的方法,需要实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称
  • 第三步提取输入内容的过程实现writeInfo函数,用于随时向output.txt输入内容
3.2实现过程
  • 由上述分析可知,需要先导入阿里巴巴的fastjson解析包,调用fastjson的jsonObject与jsonArray去解析对应数据

  • 然后调用内部api实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称的关键代码

  • writePlayers函数与writeResults函数只需要调用对应api即可

4. 关键代码展示

  • 实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称的关键代码
 //通过uuid查找队伍
     static JSONObject searchTeam(String teamUuid,JSONArray teams){
        for (int i = 0;i < teams.size();i++){
            if (teams.getJSONObject(i).getString("uuid").equals(teamUuid)){
                return teams.getJSONObject(i);
            }
        }
        return null;
    }

    //通过uuid数组查找选手
     static JSONArray searchWinners(String playerUuids,JSONArray players){
        JSONArray winners = new JSONArray();
        JSONArray uuids = JSONArray.parseArray(playerUuids);//需要查找的选手uuid
        for (int i = 0; i < uuids.size(); i++){
            String uuid = uuids.getString(i);
            for (int j = 0; j < players.size(); j++){
                if (players.getJSONObject(j).getString("uuid").equals(uuid)){
                    winners.add(players.getJSONObject(j));
                }
            }
        }
        return winners;
    }
  • 调用api实现打印全部选手的函数、
     //对应接口中的writePlayers代码如下
     static void writePlayers(String outputFile) throws Exception {
      if (cache.containsKey("players")) {
        // 重复请求
        writeInfo(cache.get("players"),outputFile);
        return;
      }
        JSONObject jsonObject = JSONObject.parseObject(getJSONStr(PLAYERS_FILES));
        JSONArray players = JSONArray.parseArray(jsonObject.getString("players"));//获取运动员json数组
        StringBuilder playersInfo = new StringBuilder();//遍历运动员数组,把相关信息加入文件输出
        for (int i = 0; i < players.size(); i++){
            JSONObject player = players.getJSONObject(i);
            playersInfo.append("full_name:" + player.getString("full_name") + "\n");
            playersInfo.append("gender:" + ("F".equals(player.getString("gender"))? "Female" : "Male") + "\n");
            playersInfo.append("nationality:" + player.getJSONObject("nationality").getString("name") + "\n");
            playersInfo.append("-----\n");
        }
        cache.put("players",playersInfo.toString());//第一次读取的时候
        writeInfo(playersInfo.toString(),outputFile);
    }

5. 性能改进

5.1 分析
  • 性能的优化主要在于I/O流的重复启动。例如如果输入的指令有重复的,则I/O流需要重复操作,不断重复代码的运行,代码运行的重复率较高
  • 输入流用FileWriter比较慢
5.2 改进
  • 使用hashMap存储key与data充当缓存机制的实现体,key对应的是players这样的命令,而data对应的是需要读取进输出文件的内容。这样每次读取指令先对hashMap中的指令进行查找,如果查找到了,则将hashMap的内容输出到输出文化,减少了代码运行的计算时间
HashMap<String,String> cache = new HashMap<>();//用于模拟缓存,反正指令重复导致的反复读写IO
 //每次读取前进行判断
if (cache.containsKey("players")) {
        // 重复请求
        writeInfo(cache.get("players"),outputFile);
        return;
}
      
cache.put("players",playersInfo.toString());//第一次读取的时候
//每次读取前进行判断
if(cache.containsKey(date)){
          writeInfo(cache.get(date),outputFile);
            return;
}
cache.put(date,resultsInfo.toString());
  • 使用BufferdWriter包装FileWriter去对输出流进行控制
//优化前
FileWriter fileWriter = new FileWriter(outputFile,true);
fileWriter.write(msg);
fileWriter.close();

//优化后
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile,true));
bw.write(msg);
bw.close();

对如图

这样测试之后改进前的程序运行时间为:

这样测试之后改进后的程序运行时间为:

速度快了将近500ms,速度快了将近5/7

6. 单元测试

测试采用java的Juit进行测试,在对应函数上写入@Test注释并按alt+enter进行maven依赖的导入

  • 对AOSearch.java的单元测试(主要对参数不同,与参数错误进行测试)
 @Test
  public void test1(){
    //错误的参数输入
    AOSearch.main(new String[]{"input.txt"});
  }
  @Test
  public void test1_1(){
    //错误的参数输入
    AOSearch.main(new String[]{"input"});
  }
  @Test
  public void test2(){
    //正确的参数个数输入
    AOSearch.main(new String[]{"input.txt","output.txt"});
  }
  @Test
  public void test2_1(){
    //正确的参数个数输入
    AOSearch.main(new String[]{"input.txt","put.txt"});
  }
  @Test
  public void test2_2(){
    //错误的参数输入,结果可以输出put文件,可正常写入,且可以用记事本查看
    AOSearch.main(new String[]{"input.txt","put"});
  }
  @Test
  public void test2_3(){
    //错误的参数输入结果可以输出put.p文件,可正常写入,且可以用记事本查看
    AOSearch.main(new String[]{"input.txt","put.p"});
  }
  @Test
  public void test3(){
    //不正确的参数输入,覆盖率较高,程序运行较为成功
    AOSearch.main(new String[]{"input.txt","output.txt","others.txt"});
  }
  @Test
  public void test3_1(){
    //不正确的参数输入,覆盖率较高
    AOSearch.main(new String[]{"input.txt","output","others.txt"});
  }

测试结果

1、对test1的测试

结果分析:class类的覆盖率50是因为,包中的测试类未实现,而方法覆盖率低是因为穿啊如的参数错误,代码执行的少,程序结束的快

2、对test1_1的测试

3、对test2的测试

4、对test2_1的测试

5、对test2_2的测试

6、对test2_3的测试

结果符合预期

  • 对Lib接口的测试(主要对,各种不一样参数的输入进行判断)
 @Test
  public void test1() {
    try{
      Lib.writePlayers("output.txt");
      Lib.writeInfo("aa","output.txt");
      Lib.writeResults("0116","output.txt");
      Lib.writeResults("0130","output.txt");
    }catch (Exception e){
      e.printStackTrace();
    }
  }

  @Test
  public void test1_1() {
    try{
      Lib.writePlayers("output");
      Lib.writePlayers("output.pp");
    }catch (Exception e){
      e.printStackTrace();
    }
  }

  @Test
  public void test1_2() {
    try{
      Lib.writeResults("Q55","output");
      Lib.writeResults("Q55","out.txt");
      Lib.writeResults("","out.txt");
    }catch (Exception e){
      e.printStackTrace();
    }
  }
  @Test
  public void test1_3() {
    try{
      Lib.writeResults("-11","out.txt");
      Lib.writeResults("-11","ahs");

    }catch (Exception e){
      e.printStackTrace();
    }
  }
  • 对test1的测试结果

  • 对test1_1的测试结果

  • 对test1_2的测试结果

  • 对test1_3的测试结果

总结:类覆盖率达到100%,是因为所以包中的类都对其进行了实现,方法覆盖率无法进一步提升是因为io流的限制

  • 实现自动单元测试

1、

2、

3、

4、挑选需要进行测试的member

7. 异常处理

  • 总体使用try…catch…finally对代码进行维护

public class AOSearch implements Lib{
    public static void main(String[] args) {
        //毫秒ms:
        long startTime=System.currentTimeMillis(); //获取开始时间
       ...
        try{...}
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("错误");
        }finally {
            long endTime=System.currentTimeMillis(); //获取结束时间
            System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
        }
    }
}
  • 还有抛出异常,返回给调用者处理
  • 例如:
static String getJSONStr(String fileName) throws IOException{
        File file = new File(fileName);
        if (file.exists()){
            //检查json文件是否存在
            FileInputStream fileInputStream = new FileInputStream(file);
            int length = fileInputStream.available();
            byte[] bytes = new byte[length];
            fileInputStream.read(bytes);
            String JSONStr = new String(bytes);//从文件中获取json字符串
            fileInputStream.close();
            return JSONStr;
        }else{
            throw new FileNotFoundException("未找到" + fileName);
        }
    }

8. 心得体会

这次作业主要的难点在于对于json数据的解析、单元测试的编写、性能的提升、还有打包文件的过程。打包文件为jar包每次都会碰到奇怪的问题,要花大量时间去处理。分析题目不难,难在对于输入各种异常结果的输出测试,文件结构相对简单,但是编写过程会遇到杂七杂八的问题需要查找与解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值