软件工程实践第二次作业


作业基本信息

这个作业属于哪个课程软件工程实践-2023学年-W班
这个作业要求在哪里软件工程实践第二次作业–个人实战
这个作业的目标完成对世界游泳锦标赛跳水项目相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序
其他参考文献

Gitcode项目地址

仓库地址

PSP表格

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

解题思路描述

流程图

流程图

问题1

功能要求是输出所有选手信息,分别要输出Full Name,Gender,Country三个部分,
我们爬取这三个部分的数据并排序,接下来就按照题目要求进行文件的读取和输出即可。

问题2

功能要求是输出决赛每个运动项目结果,先按照题目的输出格式要求准备好输出函数,
然后将项目名作为参数来查找对应的数据文件,因为不同类型的项目比赛数据文件的格式会有不同,
在输出时进行判断这个文件是否有某个部分的数据,如果有则进行输出,没有就不输出。

问题3

功能要求是输出详细结果,在问题2的基础上判断是否有"detail",
因为每个选手参加的比赛数目可能不同,所以在获取该选手参加的比赛数据时,
先看json文件中最近的那场比赛(按照初赛、半决赛、决赛),然后获取选手姓名,
再根据选手姓名去每个比赛中查找他的比赛数据,然后依次输出即可。

接口设计和实现过程

按照题目要求,接口设计为两个功能,一个是输出所有选手信息,另一个是输出每个比赛项目结果,

//输出所有选手信息
interface PlayerInfoOutput {
    void outputPlayersInfo(BufferedWriter writer) throws IOException;
}
//输出每个比赛项目结果
interface CompetitionResultOutput {
    void outputCompetition(BufferedWriter writer, String competitionName) throws IOException;
}

最后会在输出需要结果时调用相应的接口实现函数

函数设计图

函数设计图

关键代码展示

指令处理函数
 public CommandType orderCompare(String order) {
        //默认指令号
        CommandType commandType = CommandType.error;
        //读取空文件的情况
        if (order == null) {
            return commandType;
        }
        try (BufferedReader reader = new BufferedReader(new FileReader(LIST_FILE_PATH))) {

            String orders;
            //遍历指令集并比较
            while ((orders = reader.readLine()) != null) {
                //读取空行
                if (order.isEmpty()) {
                    break;
                }
                //一样的情况
                else if (orders.equals(order)) {
                    if (order.startsWith("result ")) {
                        // 删除 "result " 部分
                        order = order.substring("result ".length());
                        // 将空格替换为下划线,这样才能与枚举类型相同
                        order = order.replace(" ", "_");
                    }
                    commandType = CommandType.valueOf(order);
                    break;
                }
                //如果正确输入且有detail,排除players
                else if (order.equals(orders + " detail")&&order.startsWith("result ")) {
                    commandType = CommandType.detail;
                    break;
                }
                //不以result开头直接error
                else if (!order.startsWith("result ")) {
                    break;
                }
                //前面为result+“ ”开头,同时不以result men/women开头
                else if (order.startsWith("result ") && !order.startsWith("result women") && !order.startsWith("result men")) {
                    commandType = CommandType.na;
                    break;
                }
                //以result men/women开头,后面不符合条件
                else if (order.startsWith("result men") || order.startsWith("result women")) {
                    //当遍历到最后一条仍然未满足,则不符合条件
                    if (commandType.ordinal() == CommandType.values().length) {
                        commandType = CommandType.na;
                        break;
                    }
                    commandType = getNextCommandType(commandType);
                }

                //不一样的指令
                else {
                    commandType = getNextCommandType(commandType);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return commandType;
    }
指令执行函数
public void outPut(CommandType commandType, BufferedWriter writer) {
        try {
            switch (commandType) {
                case players:
                    outputPlayersInfo(writer);
                    break;
                case women_1m_springboard:
                case women_3m_springboard:
                case women_10m_platform:
                case women_3m_synchronised:
                case women_10m_synchronised:
                case men_1m_springboard:
                case men_3m_springboard:
                case men_10m_platform:
                case men_3m_synchronised:
                case men_10m_synchronised:
                    outputCompetition(writer, name);
                    break;
                case error:
                    outputWrongOrder(writer);
                    break;
                case na:
                    outputWrongCondition(writer);
                    break;
                case detail:
                    outputCompetition(writer, name);
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
错误处理函数
//输出无法识别的指令
    public static void outputWrongOrder(BufferedWriter writer) {
        try {
            writer.write("Error");
            writer.newLine();
            writer.write("-----");
            writer.newLine();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //输出条件不符合的指令
    public static void outputWrongCondition(BufferedWriter writer) {
        try {
            writer.write("N/A");
            writer.newLine();
            writer.write("-----");
            writer.newLine();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
接口实现
@Override
    public  void outputPlayersInfo(BufferedWriter writer) {
        try {
            // 读取JSON文件内容
            String jsonContent = new String(Files.readAllBytes(Paths.get("src/data/players.json")));
            // 将JSON字符串转换为JSONObject
            JSONArray playersArray = new JSONArray(jsonContent);
            // 遍历每个运动员的信息
            for (int i = 0; i < playersArray.length(); i++) {
                JSONObject player = playersArray.getJSONObject(i);
                // 获取运动员的信息
                String country = player.getString("country");
                String fullName = player.getString("full_name");
                String gender = player.getString("gender");
                // 将运动员信息写入文件
                writer.write("Full Name:" + fullName);
                writer.newLine();
                writer.write("Country:" + country);
                writer.newLine();
                writer.write("Gender:" + gender);
                writer.newLine();
                writer.write("-----");
                writer.newLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //输出m米跳板信息,只需修改json的文件名就可以实现全部比赛的信息输出
    @Override
    public void outputCompetition(BufferedWriter writer, String name) {
        //不包含detail
        if (!name.contains("detail")) {
            try {
                // 构建JSON文件路径
                String filePath = "src/data/" + name + ".json";

                // 读取JSON文件内容,使用缓存
                String jsonContent = readJsonFile(filePath);

                // 将JSON字符串转换为JSONObject
                JSONObject jsonData = new JSONObject(jsonContent);

                // 获取 "final"、 "semifinal"和"preliminary" 对应的JSONArray,并输出
                JSONArray finalArray = jsonData.getJSONArray("final");
                writePlayerInfo(writer, finalArray);
                if (name.contains("platform")) {
                    JSONArray semifinalArray = jsonData.getJSONArray("semifinal");
                    writePlayerInfo(writer, semifinalArray);
                }
                if (name.contains("springboard") || name.contains("platform")) {
                    JSONArray preliminaryArray = jsonData.getJSONArray("preliminary");
                    writePlayerInfo(writer, preliminaryArray);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //包含detail
        else {
            try {
                //先处理文件名,删除掉后面的"_detail"
                name = name.replace("_detail", "");
                // 构建JSON文件路径
                String filePath = "src/data/" + name + ".json";
                // 读取JSON文件内容,使用缓存
                String jsonContent = readJsonFile(filePath);
                // 将JSON字符串转换为JSONObject
                JSONObject jsonData = new JSONObject(jsonContent);
                writePlayerInfoDetail(writer, jsonData);


            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    private static void writePlayerInfo(BufferedWriter writer, JSONArray playersArray) {
        try {
            for (int i = 0; i < playersArray.length(); i++) {
                JSONObject player = playersArray.getJSONObject(i);
        
                // 获取运动员的信息
                int rank = player.getInt("rank");
                String fullName = player.getString("player_full_name");
                JSONObject divePoints = player.getJSONObject("dive_points");
        
                // 将运动员信息写入文件
                writer.write("Full Name: " + fullName);
                writer.newLine();
                writer.write("Rank: " + rank);
                writer.newLine();
                // 遍历每次跳水的得分
                writer.write("Dive Points: ");
                double totalScore = 0.0;
                for (String diveNumber : divePoints.keySet()) {
                    double diveScore = divePoints.getDouble(diveNumber);
                    totalScore += diveScore;
                    writer.write(String.format("%.2f", diveScore) + " + ");
                }
                // 输出总分
                writer.write("= " + String.format("%.2f", totalScore));
                writer.newLine();
                writer.write("-----");
                writer.newLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

性能改进

使用了hashMap进行数据缓存,避免多次重复访问json数据
    // JSON文件缓存
    private Map<String, String> jsonCache = new HashMap<>();

    // 从文件中读取JSON内容,添加缓存机制------------------------------------------------
    private String readJsonFile(String filePath) throws IOException {
        if (jsonCache.containsKey(filePath)) {
            // 如果缓存中已存在,直接返回缓存的内容
            return jsonCache.get(filePath);
        } else {
            // 读取文件内容
            String jsonContent = new String(Files.readAllBytes(Paths.get(filePath)));
            // 将内容放入缓存
            jsonCache.put(filePath, jsonContent);
            return jsonContent;
        }
    }

单元测试

按照错误类型,分成Error错误和N/A错误

测试类
public class TestMethod {
    //N/A测试
    @Test
    public void test1() throws IOException {
        String inputFilePath = "test_input.txt";
        String outputFilePath = "test_output.txt";

        // 准备多个N/A的测试数据
        String[] testInputs = {
                "result women_1m_springboard",
                "result men 3m_synchronised",
                "result men 3m synchronised ",
                "result men 3m  synchronised",
                "result men  3m synchronised",
                "result  men 3m synchronised",
                "result  men  3m synchronised",
                "result women 3m platform",
                "result women",
                "result men",
                "result men 10m springboard",
                "result men 1 springboard",
                "result me 1 springboard",
                "result me 1m springboard",
                "result women 1m springboard detail ",
        };

        try {
            // 执行多个测试,一次读一条
            for (String inputContent : testInputs) {
                // 将当前测试数据写入文件
                writeToFile(inputFilePath, inputContent);

                // 执行测试
                Lib lib = new Lib();
                lib.start(inputFilePath, outputFilePath);

                // 验证输出是否符合预期
                String expectedOutput = getExpectedOutput1(inputContent);

                String actualOutput = readFromFile(outputFilePath);
                assertEquals(expectedOutput, actualOutput);
            }
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    //error测试
    @Test
    public void test2() throws IOException {
        String inputFilePath = "test_input.txt";
        String outputFilePath = "test_output.txt";

        // 准备多个Error的测试数据
        String[] testInputs = {
                "a",
                "playerss",
                "players ",
                "players detail",
                "resultmen 1m springboard",
                "resultwomen 1m springboard",
                "aresult women 1m springboard",
                " players",
                "women_1m_springboard",
                "men_3m_synchronised",
                "unknown_command women_1m_springboard",
                "unknown_directive players",
                "unknown_result men_10m_platform",
                "unknown_info women_3m_synchronised extra_info",
                "unknown women_10m_platform",
                "additional_info",
                "",

        };

        try {
            // 执行多个测试,一次读一条
            for (String inputContent : testInputs) {
                // 将当前测试数据写入文件
                writeToFile(inputFilePath, inputContent);

                // 执行测试
                Lib lib = new Lib();
                lib.start(inputFilePath, outputFilePath);

                // 验证输出是否符合预期
                String expectedOutput = getExpectedOutput2(inputContent);

                String actualOutput = readFromFile(outputFilePath);
                assertEquals(expectedOutput, actualOutput);
            }
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }
    // 根据输入内容生成预期输出
    private String getExpectedOutput1(String inputContent) {
        return "N/A\n-----\n";
    }
    private String getExpectedOutput2(String inputContent) {
        return "Error\n-----\n";
    }

    // 将字符串写入文件
    private void writeToFile(String filePath, String content) {
        try(BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            writer.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 从文件中读取字符串
    private String readFromFile(String filePath) {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content.toString();
    }
}
test1的覆盖率

test1的覆盖率

test1的运行速度

test1的运行速度

test2的覆盖率

test2的覆盖率

test2的运行速度

test2的运行速度

TestMethod的覆盖率

testMethod的覆盖率

TestMethod的运行速度

testMethod的运行速度

异常处理

1.出现文件为空的情况时,会按照Error操作处理
2.输入文件中出现空行时,按照Error操作处理
3.除"players"以外,以"result “开头出现错误按照N/A处理
4.除以"result ”开头的,其他均按照Error处理
5.当输入文件不存在时,会提示"输入文件不存在”

心得体会

一开始看到这个题目,可能是太久没有写编程作业的原因,有点无从下手,想到哪写到哪,
写着写着就有点混乱了,后面梳理了一下流程后,按照流程开始编写就顺利很多了,这也让我意识到
设计的重要性,一开始的编程就像老师上课讲的搭狗窝式编程,想到哪写到哪,这样没有计划的编程效率确实会比较低,
有计划的编程,知道自己每一步要做什么该做什么,效率就会高效很多。
这次小练手最大的收获就是意识到设计的重要性以及使用git上传文件到远程仓库的原理有了一定的了解,操作更加熟练。

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值