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

222100209李炎东 2024-02-21 13:44:22
这个作业属于哪个课程2302软件工程社区
这个作业要求在哪里软件工程第二次作业--文件读取
这个作业的目标完成文件读取小练习
其他参考文献《构建之法》、《阿里巴巴Java开发手册》工程师的能力评估和发展单元测试和回归测试

目录

  • Gitcode项目地址
  • PSP表格
  • 解题思路描述
  • 数据获取
  • 数据处理
  • 数据缓存
  • 接口设计和实现过程
  • 项目结构
  • 核心功能接口设计
  • 核心功能实现过程
  • 关键代码展示
  • 网络请求
  • 处理数据
  • 命令处理
  • 性能改进
  • 单元测试
  • 异常处理
  • 网络环境异常
  • 文件不存在异常
  • 心得体会

Gitcode项目地址

项目地址

PSP表格

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

解题思路描述

数据获取

从题目中提供的网站,打开开发者工具初步判断对应的请求,并获取url地址已经接口参数等进行分析,后续在程序中使用网络请求获取数据(预计同时也会提供一种允许通过读取本地数据(example中的data)实现)

运动员数据相对简单,直接可以找到对应的api

img

从这个请求对应的请求头中可以找到我们需要的url,即https://api.worldaquatics.com/fina/competitions/3337/athletes?gender=&countryId=,分析可,这是一个GET请求,带有gender、countryId等params,params参数可以为空

比赛数据涉及到多个api请求,我们首先在请求中找到比赛项目以及对应的id,我们可以轻易的找到获取这些信息的api接口: https://api.worldaquatics.com/fina/competitions/3337/events

img

接着我们进一步找到具体比赛项目的详细得分情况,api: https://api.worldaquatics.com/fina/events/${id},其中id就是刚刚从请求中得到的id

img

网络请求使用hutool的网络请求包进行(这也是java开发用的较多的开源工具之一了)

数据处理

使用hutool的json库对请求返回的json数据进行解析,同时需要编写JavaBean来对Json数据做一些简单的映射,便于后续开发和维护使用。

数据缓存

为提高系统性能,使用hutool的缓存组之间本地缓存,加速读取热点数据的过程,避免网络请求或文件流读取成为系统应用瓶颈。

(不用redis是嫌麻烦了,而且怕待会助教整起来又没环境什么的)

接口设计和实现过程

项目结构

.
├─assets
└─src
   ├─core        // 核心功能接口设计
   │  └─impl        // 核心功能实现类
   ├─datas        // 本地缓存数据
   ├─entity        // 实体对象封装
   └─util        // 工具类

核心功能接口设计

/**
 * 核心算法接口定义
 *
 * @author 天狗
 * @version 1.0
 * @date 2024/2/19 11:25
 */
public interface Algorithms {

    /**
     * 获取所有选手信息
     * @return  选手信息列表
     */
    List<Player> getAllPlayers();

    /**
     * 获取指定项目的决赛结果
     * @param event 项目
     * @return  比赛结果列表
     */
    List<Result> getResults(String event);

    /**
     * 获取指定项目的详细结果
     * @param event 项目
     * @return  比赛结果列表
     */
    List<Result> getDetailResults(String event);

}

核心功能实现过程

  • 使用Hutool的网络请求工具,对目标api的url进行请求,初步获得原始json数据
  • 使用Hutool的Json工具,处理并提取需要的数据,封装到对象中
    • 获取所有选手信息比较简单,只要从一个请求中就能获取到所有的数据

关键代码展示

网络请求

/**
 * 基于hutool的Http请求工具类
 *
 * @author 天狗
 * @version 1.0
 * @date 2024/2/18 16:12
 */
public class HttpRequestUtil {

    /**
     * 发送get请求
     * @param url   请求地址
     * @param queryParams   请求参数
     * @return  返回json对象
     * @throws IOException  IO异常
     */
    public static JSONObject getObj(String url, Map<String, Object> queryParams) throws IOException {
        return HttpRequestUtil.getObj(url, queryParams, new HashMap<String, String>(1));
    }

    /**
     * 携带自定义请求头发送get请求
     * @param url   请求地址
     * @param queryParams   请求参数
     * @param headers   请求头
     * @return  返回json对象
     * @throws IOException  IO异常
     */
    public static JSONObject getObj(String url, Map<String, Object> queryParams, Map<String, String> headers) throws IOException {
        String body = HttpRequestUtil.get(url, queryParams, headers);
        return JSONUtil.parseObj(body);
    }

    /**
     * 发送get请求
     * @param url   请求地址
     * @param queryParams   请求参数
     * @return  返回json数组
     * @throws IOException  IO异常
     */
    public static JSONArray getArr(String url, Map<String, Object> queryParams) throws IOException {
        return HttpRequestUtil.getArr(url, queryParams, new HashMap<String, String>(1));
    }

    /**
     * 携带自定义请求头发送get请求
     * @param url   请求地址
     * @param queryParams   请求参数
     * @param headers   请求头
     * @return
     */
    public static JSONArray getArr(String url, Map<String, Object> queryParams, Map<String, String> headers) {
        String body = HttpRequestUtil.get(url, queryParams, headers);
        return JSONUtil.parseArray(body);
    }

    /**
     * 发送get请求
     * @param url   请求地址
     * @param queryParams   请求参数
     * @param headers   请求头
     * @return  返回json字符串
     */
    private static String get(String url, Map<String, Object> queryParams, Map<String, String> headers) {
        return HttpRequest.get(url).form(queryParams).addHeaders(headers).execute().body();
    }
    
    /**
     * 发送post请求
     * @param url   请求地址
     * @param json  请求体
     * @param headers   请求头
     * @return  返回json对象
     */
    public static JSONObject post(String url, String json, Map<String, String> headers) {
        String body = HttpRequest.post(url).body(json).addHeaders(headers).execute().body();
        return JSONUtil.parseObj(body);
    }

}

处理数据

代码量较多,这边只展示一部分

    /**
     * 处理结果数据
     * @param resultData    结果数据
     * @param key   key
     * @param mergeMap  合并结果
     * @param phase 阶段
     */
    private void processResultData(JSONObject resultData, String key, Map<String, Result> mergeMap, String phase) {
        if (resultData != null) {
            for (Object o : resultData.getJSONArray(key)) {
                JSONObject player = (JSONObject) o;
                Result result = new Result();
                String fullName = player.getStr("FullName");
                String rank = player.getStr("Rank");
                ArrayList<String> scoreList = new ArrayList<>();
                for (Object dive : player.getJSONArray("Dives")) {
                    JSONObject diveObj = (JSONObject) dive;
                    scoreList.add(diveObj.getStr("DivePoints"));
                }

                Result mapResult = mergeMap.get(fullName);
                if (mapResult != null) {
                    BeanUtil.copyProperties(mapResult, result);
                }
                result.setFullName(fullName);
                switch (phase) {
                    case "Final":
                        result.setFinalRank(rank);
                        result.setFinalScore(scoreList);
                        break;
                    case "SemiFinal":
                        result.setSemiFinalRank(rank);
                        result.setSemiFinalScore(scoreList);
                        break;
                    case "Preliminary":
                        result.setPreliminaryRank(rank);
                        result.setPreliminaryScore(scoreList);
                        break;
                    default:
                        break;
                }
                mergeMap.put(fullName, result);
            }
        }
    }

命令处理

/**
 * 执行读入的命令命令
 * @param command   命令
 * @throws IOException  IO异常
 */
private static String doCommand(String command) {
    Algorithms algorithms = new AlgorithmsImpl();
    if ("players".equals(command)) {
        List<Player> players = algorithms.getAllPlayers();
        return toResultString(players);
    } else if (Pattern.matches("result .* detail$", command)) {
        String event = command.replaceFirst("result ", "").replaceFirst(" detail", "");
        List<Result> results = algorithms.getDetailResults(event);
        return toDetailResultString(results);
    } else if (Pattern.matches("result .*",command)) {
        String event = command.replaceFirst("result ", "");
        List<Result> results = algorithms.getResults(event);
        return toResultString(results);
    }  else {
        return "ERROR\n-----\n";
    }
}

性能改进

增加了一个缓存工具类,基于Hutool提供的LFU最少使用率策略,使用单例模式,保证全局实例唯一性

/**
 * 缓存工具类
 * 用于缓存一些数据
 * 使用单例模式
 *
 * @author 天狗
 * @version 1.0
 * @date 2024/2/20 14:55
 */
public class CacheSingleton {

    private static volatile Cache<String, Object> instance;

    public static Cache<String, Object> getInstance() {
        if (instance == null) {
            synchronized (CacheSingleton.class) {
                if (instance == null) {
                    instance = CacheUtil.newLRUCache(100);
                }
            }
        }
        return instance;
    }

    public static void put(String key, Object value) {
        getInstance().put(key, value);
    }

    public static Object get(String key) {
        return getInstance().get(key);
    }
    
}

单元测试

对核心模块进行单元测试,测试覆盖率要求达到75%以上

img

img

对入口函数进行测试,覆盖率同样符合要求

img

img

异常处理

原本想搞全局异常捕获的,但是这种相对原生的项目还是有点无从下手

网络环境异常

当Http请求失败时,会捕获异常,并从本地读取json数据进行输出。(数据来源于题目example中提供的datas)

文件不存在异常

会在控制台中进行提示

心得体会

在这个作业中,我学会了如何从互联网上获取数据,然后用程序来分析和处理这些数据。最难的部分是理解如何正确地使用API来获取数据,并且搞清楚如何从复杂的JSON格式中提取我们需要的信息。另外,学会了如何从文件中读取数据,处理它们,然后将结果保存回文件。这个作业让我更熟练地掌握了API调用、JSON处理和文件操作,也让我更有信心能够在未来的项目中应对类似的挑战。通过这次经历,我觉得自己的编程能力得到了提升,也更了解了软件工程这个领域的实际应用。

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

112

社区成员

发帖
与我相关
我的任务
社区描述
FZU-SE
软件工程 高校
社区管理员
  • LinQF39
  • 助教-吴可仪
  • 一杯时间
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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