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

222000201鲍仁俊 学生 2023-03-02 22:07:35

一、作业概览

1.作业的基本信息

这个作业属于哪个课程2023年福大-软件工程实践-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标1.完成对澳大利亚网球公开赛相关数据的收集
2.实现一个能够对赛事数据进行统计的控制台程序
其他参考文献暂无

目录

  • 一、作业概览
  • 1.作业的基本信息
  • 二、作业详情
  • 1. Gitcode项目地址
  • 2. PSP表格
  • 3. 描述解题思路
  • 4. 接口设计和实现过程
  • 4.1 选手类
  • 4.2 组队类(用于中间处理过程)
  • 4.3 结果类
  • 4.4 指令类
  • 4.5 文件类
  • 5. 性能改进
  • 6. 单元测试
  • 7. 异常处理
  • 8. 心得体会

二、作业详情

1. Gitcode项目地址

https://gitcode.net/mikupi/project-java

2. PSP表格

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

3. 描述解题思路

  1. 首先需要获取数据,要从官方网站上导出相关的数据信息作为程序的数据源。题目中已经给出了网站,直接打开网站,打开Web控制台,点击菜单栏里的ResultPlayers。然后把这些数据打包下载到本地。

    配图

  2. 有很多方法可以处理json数据,比如常用的Gson,但是我选择了JSONObject去处理,用到的是org.json.simple这个jar包。
  3. 对于players的处理,直接获取数据遍历输出即可。
  4. 对于result的处理,略微复杂,根据json数据结构的分析,得先获取teams的编号,然后通过matches中每场比赛的数据里提取winnertrue的队伍。同时在遍历的时候进行记录比分和比赛时间。其次记录下队伍编号,通过队伍的编号到teams中获取队伍的成员编号,最后通过成员编号结合players得到成员名。

4. 接口设计和实现过程

4.1 选手类
  1. Player类:用于存放必要的选手数据,比如:编号全名国籍性别。拥有接口:获取数据的接口。
  2. Players类:拥有Player类数组。实现输出所有Player和根据编号查询Player全名的功能。
  3. 结构和关键代码展示
public class Players {
    public ArrayList<Player> players;
    public Players() {players = new ArrayList<Player>();}
    public void add(Player p) {players.add(p);}
    public int len() {return players.size();}
    
    public String get(int i) {
        String res = "";
        if (this.len() > i) {
            res = res + "full_name:" + players.get(i).getFullname() + "\n";
            res = res + "gender:" + players.get(i).getGender() + "\n";
            res = res + "nationality:" + players.get(i).getNationality();
        }
        return res;
    }

    //获取所有信息的输出结果
    public String getAll() {
        StringBuilder res = new StringBuilder();
        if (this.len() != 0) {
            for (int i = 0; i < this.len(); i++) {
                if (i != 0) {
                    res.append("\n");
                }
                res.append(this.get(i)).append("\n").append("-----");
            }
        }
        return res.toString();
    }

    //通过player的id去获取相应的player全名
    public String getNameById(String uid) {
        int l = 0;
        int r = this.len() - 1;
        int m;
        while(l <= r) {
            m = (l + r) / 2;
            if (players.get(m).getUuid().compareTo(uid) < 0) {
                l = m + 1;
            } else if (players.get(m).getUuid().compareTo(uid) == 0) {
                return players.get(m).getFullname();
            } else {
                r = m - 1;
            }
        }
        return "";
    }

    public void Sort(){Collections.sort(this.players);}
}

public class Player implements Comparable<Player>{
    private String fullname;
    private String gender;
    private String nationality;
    private String uuid;

    public Player(String fn, String g, String na, String uuid){
        this.fullname = fn;
        if (g.equals("M") || g.equals("m")) {
            this.gender = "male";
        } else {
            this.gender = "female";
        }
        this.nationality = na;
        this.uuid = uuid;
    }

    public String getFullname() {return this.fullname;}
    public String getGender() {return this.gender;}
    public String getNationality() {return this.nationality;}
    public String getUuid() {return this.uuid;}
    @Override
    public int compareTo(Player o) {return (this.uuid).compareTo(o.uuid);}
}
4.2 组队类(用于中间处理过程)
  1. Team类:用于存放队伍信息,比如:队伍编号队伍下的队员编号。拥有获取队伍信息的接口。
  2. Teams类:拥有Team类数组。实现根据队伍编号查询成员的功能。
  3. 结构展示
public static class Teams{
    private ArrayList<Team> teams;
    public Teams(){}//初始化
    public void add(Team t){}//动态添加组
    public String getWinnerById(String id, Players p){}//根据队伍编号,结合Player数据p得到winner的全名信息
}
public static class Team{
    private String tid;//队伍编号
    private ArrayList<String> playid;//成员编号
    public Team(String t){}//初始化
    public void add(String pid){}//动态添加成员
    public String getTid(){}
    public String getWinner(Players p){}//根据Player信息得到赢家的全名
}
4.3 结果类
  1. Result类:用于存放一次比赛的结果信息,比如:时间获胜者比分,拥有获取数据的接口。
  2. Results类:拥有Result类数组,实现输出所有的比赛结果。
  3. 结构展示
public static class Results{
    private ArrayList<Result> results;
    public Results(){}//初始化
    public void add(Result t){}//动态添加结果项
    public int len(){}//结果数
    public String get(int i){}//某一条结果
    public String getAll(){}//获取所有结果标准化输出
}

public static class Result{
    private String time;
    private String winner;
    private String score;
    public Result(String t, String w, String s){}//初始化
    public String getTime(){}
    public String getWinner(){}
    public String getScore(){}
}
4.4 指令类
  1. Instructions类:用于存放指令,实现对指令的判断。
  2. 结构及关键代码展示
public static class Instructions{
    private ArrayList<String> instructions;
    public Instructions(){}//初始化
    public void add(String s){}//动态添加指令
    public String get(int i){}//获取某条指令
    public int len(){}//指令数
    public static boolean isin(String[] arr, String containValue){}//字符串处理函数
    //判断指令类型输出相应处理代号
    public String read(String s, String[] j) {
        //处理指令
        if (s.equals("players")) {
            return "2";
        }
        if (s.length() < 7) return "0";
        String front = s.substring(0, 7);
        String end = s.substring(7);
        if (!front.equals("result ")) {
            return "0";
        }
        if (Lib.isin(j, end)) {
            return end;
        }
        return "1";
    }
}
4.5 文件类
  1. MyFile类:实现解析json文件,提取和处理信息输出为相应的Results类实例Players类实例。实现对input.txt文件的处理,输出为Instructions类的实例。
  2. 结构和关键代码展示
public class MyFile {
    public MyFile() {}

    //根据文件解析读取输出Players
    public Players ReadPlayer(String pa) throws IOException, ParseException {
        Object obj = new JSONParser().parse(new FileReader(pa));
        JSONObject js = (JSONObject) obj;
        JSONArray players_arr = (JSONArray) js.get("players");
        Players players = new Players();
        int players_len = players_arr.size();
        for (int i = 0; i < players_len; i++) {
            JSONObject t = (JSONObject) players_arr.get(i);
            players.add(parsePlayer(t));
        }
        return players;
    }

    //将获取的数据提取为Player对象
    private static Player parsePlayer(JSONObject obj) {
        String full_name = (String) obj.get("full_name");
        String gender = (String) obj.get("gender");
        JSONObject nationality = (JSONObject) obj.get("nationality");
        String nationality_name = (String) nationality.get("name");
        String uuid = (String) obj.get("uuid");
        return new Player(full_name, gender, nationality_name, uuid);
    }
    
    //读取指令文件,拆分为指令列
    public Instructions ReadInput(String pa) {
        Instructions in = new Instructions();
        File file = new File(pa);
        BufferedReader reader = null;
        String temp = null;
        int line = 1;
        try {
            reader = new BufferedReader(new FileReader(file));
            while ((temp = reader.readLine()) != null) {
                in.add(temp);
                line++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return in;
    }
    
    //根据文件解析读取输出Results
    public Results ReadResult(String pa) throws IOException, ParseException {
        Players p = ReadPlayer(pa);
        p.Sort();
        Object obj = new JSONParser().parse(new FileReader(pa));
        JSONObject js = (JSONObject) obj;
        JSONArray match_arr = (JSONArray) js.get("matches");
        JSONArray team_arr = (JSONArray) js.get("teams");
        int team_arr_len = team_arr.size();
        Teams teams = new Teams();
        for (int i = 0; i < team_arr_len; i++) {
            JSONObject t = (JSONObject) team_arr.get(i);
            teams.add(parseTeam(t));
        }
        teams.Sort();

        int match_arr_len = match_arr.size();
        Results results = new Results();
        for (int i = 0; i < match_arr_len; i++) {
            JSONObject t = (JSONObject) match_arr.get(i);
            JSONObject iswo = (JSONObject) t.get("match_status");
            String abbr = (String) iswo.get("abbr");
            if (abbr.equals("W/O")) {
                results.add(new Result("", "", "", true));
                continue;
            }
            String time = (String) t.get("actual_start_time");
            JSONArray ts = (JSONArray) t.get("teams");
            String win_tid = "";
            String score = "";
            JSONObject t1 = (JSONObject) ts.get(0);
            JSONObject t2 = (JSONObject) ts.get(1);
            JSONArray t1_s = (JSONArray) t1.get("score");
            JSONArray t2_s = (JSONArray) t2.get("score");
            int wincount = 0;
            for (int j = 0; j < t1_s.size(); j++) {
                JSONObject t1_j = (JSONObject) t1_s.get(j);
                JSONObject t2_j = (JSONObject) t2_s.get(j);
                Boolean iswinner1 = (Boolean) t1_j.get("winner");
                if (iswinner1) {
                    wincount++;
                } else {
                    wincount--;
                }
                String s_1 = (String) t1_j.get("game");
                String s_2 = (String) t2_j.get("game");
                if (!score.equals("")) score = score + " | ";
                score = score + s_1 + ":" + s_2;
            }
            if (wincount > 0) {
                win_tid = (String) t1.get("team_id");
            } else {
                win_tid = (String) t2.get("team_id");
            }
            String win = teams.getWinnerById(win_tid, p);
            results.add(new Result(time, win, score));
        }
        return results;
    }

    //将获取的数据提取为Team对象
    public static Team parseTeam(JSONObject obj) {
        Team t;
        String tid = (String) obj.get("uuid");
        JSONArray players = (JSONArray) obj.get("players");
        t = new Team(tid);
        for (int i = 0; i < players.size(); i++) {
            String tt = (String) players.get(i);
            t.add(tt);
        }
        return t;
    }
}

5. 性能改进

  1. 将获得的信息[Team,Player]根据id编号进行排序,在后面比对的时候不用顺序遍历比较,而采用二分查找算法进行优化,加快比对获取结果的速度。
//更改前
public String getNameById(String uid) {
    for (int i = 0; i < this.len(); i++) {
        if (players.get(i).getUuid().equals(uid)) {
            return players.get(i).getFullname();//获取全名
        }
    }
    return "信息比对出错";
}

//更改后【此处代码中省略了对compareTo接口的实现】
public String getNameById(String uid) {
    int l = 0;
    int r = this.len() - 1;
    int m;
    while(l <= r) {
        m = (l + r) / 2;
        if (players.get(m).getUuid().compareTo(uid) < 0) {
            l = m + 1;
        } else if (players.get(m).getUuid().compareTo(uid) == 0) {
            return players.get(m).getFullname();
        } else {
            r = m - 1;
        }
    }
    return "信息比对出错";
}

更改后速度有了略微的提升,这里用100次的叠加运行来观测。

  • 更改前

    更改前

  • 更改后

    更改后

  1. 应用StringBuilder类来处理字符串数据的串接。
//更改前
public String getAll() {
    String res = "";
    if (this.len() != 0) {
        for (int i = 0; i < this.len(); i++) {
            if (i != 0) {
                res = res + "\n";
            }
            res = res + this.get(i) + "\n" + "-----";
        }
    }
    return res;
}

//更改后
public String getAll() {
    StringBuilder res = new StringBuilder();
    if (this.len() != 0) {
        for (int i = 0; i < this.len(); i++) {
            if (i != 0) {
                res.append("\n");
            }
            res.append(this.get(i)).append("\n").append("-----");
        }
    }
    return res.toString();
}

更改后速度有了很大的提升。

  • 更改前

    更改前

  • 更改后

    更改后


6. 单元测试

导入junit4进行单元测试 对AOSearchTest作为AOSearch类的测试类进行单元测试

在这里插入图片描述

查看了代码情况后,未覆盖的主要都是异常捕获处理部分的代码。


7. 异常处理

  1. 输入部分判定:判断是否为两个参数,如果不是则输入错误信息结束。

    请添加图片描述

  2. IO异常:涉及文件IO的部分会有try-catch包裹,若文件不存在,则throw该异常,catch后输出相关错误信息。

  3. 程序中的查找比对错误,没有查到结果将会返回错误信息字符串,但由于不会影响输出和破坏结构因此我将其放入了结果的字符串中

    请添加图片描述


8. 心得体会

  本来学完java我就把eclips卸载了,因为这个作业,有幸马上有熟悉了一遍java的环境配置并尝试了新的写代码的软件——IDEA,才发现这个软件真的还挺好用的,当然为了使用也熟悉了一遍这个软件。其次,以前都不怎么用Gitcode这类的网站托管代码,因为作业,也熟悉了一遍相关的操作。再看到内容上,此次作业不仅让我熟悉了jar包的导入和调用,也了解了单元测试是如何进行的以及具体的一些操作细节。PSP表也是第一次见,感觉对于项目的规划还是非常有用也非常有帮助。总而言之,不管结果如何,学到的东西还是挺多的,也尝试了很多以前没尝试过的东西。

...全文
166 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
助教-吴雨薇 助教 2023-03-12
  • 打赏
  • 举报
回复

博客排版清楚,各部分完成的不错,继续加油

688

社区成员

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

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