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

022000232沈向炜 学生 2023-03-02 21:02:53
2023春软件工程实践2023年福大-软件工程实践-W班社区-CSDN社区云
这个作业要求在哪里软件工程实践第二次作业——个人实战-CSDN社区
这个作业的目标根据命令行输入,输出2023年澳大利亚网球公开赛的选手信息和比赛结果。
其他参考文献构建之法

目录

  • 1. Gitcode项目地址
  • 2. PSP表格
  • 3. 解题思路描述
  • 3.1 数据来源与获取方法
  • 3.2 解析json文件
  • 3.3 对象读取
  • 4. 接口设计和实现过程
  • 4.2 工具类
  • 4.3 入口接口
  • 4.4 程序流程图
  • 关键代码展示
  • 5.1读取Json文件
  • 5.2读取选手信息
  • 5.3读取比赛日结果
  • 5.4 处理指令队列
  • 6. 性能改进
  • 7. 单元测试
  • 8. 异常处理
  • 9. 心得体会

1. Gitcode项目地址

仓库地址

2. PSP表格

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

3. 解题思路描述

3.1 数据来源与获取方法

​ 任务一的数据选手数据下载与群文件,任务二的赛事结果数据爬取自官网,打开AOS官网,找到result页面,打开浏览器开发者模式的网络模块,再点击对应的比赛日,从开发者工具中找到result文件,将其另存为json。

3.2 解析json文件

​ 先采用文件输入流打开json文件并转存为字符串

​ 可以采用第三方库Gson的fromjson来将json转存的字符串转换成实体类,为了能够对json的数据的字段进行查找,所以将json转成Map<String,Object>,然后逐层用get方法获得所需字段。

3.3 对象读取

​ 由于比赛的选手和场数数量庞大,反复读取json文件效率低效,又因为需要通过时间反复查找对应比赛日的结果集,通过选手id找出选手的名字缩写,所以设计map结构来存取信息备用。

​ 同时赛事结果文件中只有队伍id,而队伍选手id列表存在与players.json,为了方便查找获胜选手的名字缩写需要建立team_id到选手id的映射。

​ 当程序需要打印比赛选手信息时,直接打印选手map的values();

​ 需要打印比赛日或者赛季信息时,初次打印先解析json并放入map,再从map取出

...

4. 接口设计和实现过程

...4.1 主要类

//程序入口,主函数接收输入输出文件的路径
public class AOSSearch {}
//工具类,负责代码运行逻辑
public class Lib {}
//实体类,存放比赛结果信息
class AOSGame {}
//实体类,存放选手信息
class AOSPlapyer {}

4.2 工具类

public class Lib {
    //存放选手信息
    public static Map<String,AOSPlayer> Player= new HashMap<>();
    //存放每一天的赛程结果
    public static Map<String,ArrayList<AOSGame>> Game=new HashMap<>();
    //存放每一行的指令
    public static ArrayList<String> orders=new ArrayList<>();
    //存放队伍id到选手id列表的映射
    public static Map<String,List<String>> Team2Player= new HashMap<>();
    //存放选手和赛程的文件位置
    public static final String PLAYERPATH ="src\\data\\players.json";
    public static final String  GAMEERPATH= "src\\data\\schedule\\";
    
    //读取input中的指令并存储到orders数组中
    public static void read(String inputFilePath) throws IOException{...}
    //读取json文件中的数据并返回对应字符串
    public static String readJson(String jsonFilePath) throws FileNotFoundException,IOException{...}
    //将从json文件中读取出的字符串进一步处理并存储到Player中
    public static void readPlayer() throws IOException{...}
    //从Team2Player中找到获胜选手缩写的拼接
    public  static String  getWinner(String team_id) throws IOException{
    //将从json文件中读取出的字符串进一步处理并存储到Game中
    public static void readGame(String date) throws IOException{...}
    //从map中解析出结果对象
    public  static AOSGame getAOSGame(Map<String,Object> a) throws IOException{...}
    //将选手信息输出到对应的文件
    public static void writePlayers() throws IOException{...}
    //将赛程结构输出到对应的文件
    public static void writeGame(String date) throws IOException {...}
    //将无法输出的情况输出
    public static void writeError(String message) throws IOException{...}
    //对于程序是否在比赛日中
    public static boolean hasDate(String date){...}
}

4.3 入口接口

public class AOSSearch {
    //捕获各类异常
    public static void main(String[] args) {
        try {
            Lib.read(args[0]);
            Lib.write(args[1]);

        }
        catch (FileNotFoundException fileNotFoundException){
            System.out.println("文件不存在");
        }
        catch (IOException ioException){
            System.out.println("文件读写失败");
        }
    }
}

4.4 程序流程图

img

关键代码展示

5.1读取Json文件

    public static String readJson(String jsonFilePath) throws FileNotFoundException,IOException{
        StringBuilder data=new StringBuilder();
        String tem;
        InputStream file;
        jsonFilePath=System.getProperty("user.dir")+File.separator+jsonFilePath;
        file=new FileInputStream(jsonFilePath);
        BufferedReader reader=new BufferedReader(new InputStreamReader(file,StandardCharsets.UTF_8));
            while((tem=reader.readLine())!=null){
            data.append(tem);
        }
        reader.close();
        return data.toString();
    }

5.2读取选手信息

 //将从json文件中读取出的字符串进一步处理并存储到Player中
    public static void readPlayer() throws IOException{
        String json=readJson(PLAYERPATH);
        Gson gson=new Gson();
        Map<String,Object> data=gson.fromJson(json,Map.class);
        List<Map<String,Object>> PlayerList=(List<Map<String,Object>>) data.get("players");
        for(Map<String,Object> m:PlayerList){
            AOSPlayer o=new AOSPlayer((String)m.get("full_name"),(String)m.get("gender"),((Map<String,String>)m.get("nationality")).get("name"),(String) m.get("uuid"),(String) m.get("short_name"));
            Player.put((String) m.get("uuid"),o);
        }
        List<Map<String,Object>> TeamList=(List<Map<String,Object>>) data.get("teams");
        for(Map<String,Object> t:TeamList){
            List<String> pl = (List<String>)t.get("players");
            String tid = (String)t.get("uuid");
            Team2Player.put(tid,pl);
        }
    }

5.3读取比赛日结果

//将从json文件中读取出的字符串进一步处理并存储到Gamepublic static void readGame(String date) throws IOException{
    ArrayList<AOSGame> s=new ArrayList<>();
    String json=readJson(GAMEERPATH+date+".json");
    Gson gson=new Gson();
    Map<String,Object> map=gson.fromJson(json,Map.class);
    List<Map<String,Object>> matches=(List<Map<String,Object>>) map.get("matches");
    for(Map<String,Object> a : matches)
       s.add(getAOSGame(a));
    Game.put(date,s);
} public  static AOSGame getAOSGame( Map<String,Object> a) throws IOException{
        String time = (String) a.get("actual_start_time");
        String  winner;
        Map<String,String> stats =(Map<String,String>) a.get("match_status");
        //比赛有人弃权
        if(stats.get("abbr").equals("W/O"))
            return new AOSGame();
        List<Map<String,Object>> teams = (List<Map<String,Object>>) a.get("teams");
        Map<String,Object> t1 = teams.get(0);
        Map<String,Object> t2 = teams.get(1);
        List<Map<String,String>> s1 =(List<Map<String,String>>) t1.get("score");
        List<Map<String,String>> s2 =(List<Map<String,String>>) t2.get("score");
        String score="";
        for(int i =0;i<s1.size();i++){
            score+=(s1.get(i).get("game")+":"+s2.get(i).get("game"))+" | ";
        }
        score=score.trim();
        score=score.substring(0,score.length() - 1);
        if(t1.get("status")!=null)
            winner=getWinner((String) t1.get("team_id"));
        else
            winner=getWinner((String) t2.get("team_id"));
        return  new AOSGame(winner,score,time);
    }
     //map中解析出结果对象
    public  static AOSGame getAOSGame( Map<String,Object> a) throws IOException{...}

5.4 处理指令队列

    public static void write(String outputFilePath) throws IOException{
        FileOutputStream file=new FileOutputStream(outputFilePath,false);
        readPlayer();
        writer=new BufferedWriter(new OutputStreamWriter(file,StandardCharsets.UTF_8));
        for(String o:orders){
            o=o.trim();
            if(o.equals("")){
                continue;
            }
            String[] tem=o.split("\\s+");
            if(tem.length==0){
                continue;
            }
            if(tem[0].equals("players")){
                if(tem.length==1){
                    writePlayer();
                }
                else{
                    writeError("Error");
                }
            }
            else if(tem[0].equals("result")){
                if(tem.length==2&&hasDate(tem[1])){
                    writeGame(tem[1]);
                }
                else {
                    writeError("N/A");
                }
            }
            else{
                writeError("Error");
            }
        }
        writer.close();
        //对输出文件的尾部进行处理(换行处理)
        RandomAccessFile f = new RandomAccessFile(outputFilePath, "rw");
        f.setLength(f.length()-1);
        f.close();
    }

6. 性能改进

性能改进主要从以下几点

  1. 在json转字符串时,使用StringBuilder来拼接String,提升速度
  2. 用BufferReader包装文件流,利用缓冲机制提升I/O性能
  3. 尽量写明泛型的参数,减少装箱和拆箱的消耗
  4. 将选手和结构用数据结构存储,这样文件只需读取一遍,减少I/O消耗
  5. 加入队伍id和选手id列表映射的,让

改进前

img

改进后

img

7. 单元测试

​ 单元测试利用Junit插件生成Lib类的测试函数,编辑10个测试用例主要利用assertEqual()来验证函数结果是否与正确结果相同。
可见除去异常处理语句,都有覆盖

img

8. 异常处理

在Lib中有可能出现的异常是FileNotFoundException和IOException,分别是因为文件不存在和文件流读写不成功,因为在整个运行周期有多处可能会出现这两个异常,所以我统一将Lib类产生的异常抛出,由AOSSearch处理

public static void main(String[] args) {
        try {...}
        catch (FileNotFoundException fileNotFoundException){...}
        catch (IOException ioException){...}
    }

9. 心得体会

  • 通过这次作业,我学会了网络资源爬取和Json数据的提取等技术。
  • 第一次明确规定了代码规范的写项目,发现纠正了一些之前命名的不良习惯
  • 了解到了看好问题需求和做好规划非常重要,关于需求的每一个字眼都要关注,不能想当然,可以避免无用功。
  • 第一次接触到PSP表格,性能分析,单元测试和异常处理能力,日后有继续加强。
  • 要多与周围的同学进行沟通交流,吸收他人的思考角度和方式,互相提高编码的水平。
  • 善用网络和书籍资源,加强知识储备。
...全文
547 2 打赏 收藏 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复

整体设计思路清晰,赞!能否给出性能优化中改进前后的的对比图是针对多少指令行进行的测试结果?

助教-张思锐 助教 2023-03-08
  • 打赏
  • 举报
回复

理论知识积累丰富,能正确处理编程中的一些问题,再接再厉!

688

社区成员

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

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