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

222000234张程越 学生 2023-03-02 22:59:46
这个作业属于哪个课程2023春季软件工程实践-W班(福州大学)
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标完成对澳大利亚网球公开赛相关数据的收集、掌握json数据的获取和解析、掌握java文件的输入输出、掌握java项目打jar包的方法
其他参考文献JAVA之单元测试:Junit框架软件测试开发实战|Java版本,Gson解析json比较常用的2种方式

目录

  • 1.Gitcode项目地址
  • 2.PSP表格
  • 3.解题思路
  • 3.1 如何实现input.txt文件内容输入和将打印字符输出到output.txt文件
  • 3.2 如何获取json数据
  • 3.3 如何解析json数据
  • 4.设计实现过程
  • 4.1输入输出实现
  • 4.2 对input.txt输入内容进行判断
  • 4.3 对json数据进行解析并将信息存入实体类后输出
  • 5.关键代码展示
  • 6.性能改进
  • 7.单元测试
  • 7.1 测试代码
  • 7.2 覆盖率
  • 8.异常处理
  • 9.心得体会

1.Gitcode项目地址

Chyu__/Project-Java

2.PSP表格

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

3.解题思路

3.1 如何实现input.txt文件内容输入和将打印字符输出到output.txt文件

在main函数参数String[] args中,将intput.txt文件的路径赋值给args[0],output.txt文件的路径赋值给args[1]。通过Files.readAllLines()方法来获取input.txt文件中的每一行,再将每一行的内容传入自定义的一个内容分析方法进行判断;通过PrintStream对象的构造函数可以将System.out.println()的打印内容输出到output.txt。

3.2 如何获取json数据

在我最开始读完作业要求后,因为粗心没有看见助教已经提供了部分json数据,我错误地认为所有的json数据都要通过http从澳网的网站上获取。所以我最开始先寻找关于java的okhttp是如何使用的一些参考资料。

(1)通过url请求获取数据:

在澳网的官网中点击schedule模块(后续因为助教更新的json数据是从result模块的接口拿到的数据,所以我后来接口也做了更改,但获得URL请求的方法相同),键盘按下F12打开开发者工具,在开发者工具中点击网络(Network),再点击Fetch/XHR,刷新,可以看到开发者工具里列出了页面的请求列表,找到schedule,点击右侧Headers,在General区域中就可以看到请求的url(Request URL)。(图示以Day14为例)

img

对url的处理:

我新建了一个类叫UrlList,分别用来存放players、Q、Day这三类请求的url,但对于Day和Q的URL请求需要做一些处理,因为Day和Q有多个不同日期。

img

用*代替天数所在的位置,后续只需要用字符串替换的方法将对应的天数把"*"号替换掉就行。

最后通过URL对象和HttpURLConnection对象就可以读取到相应json数据。

(2)通过复制Response获得json数据

在完成所有功能之后我发现用URL请求json数据的方法在运行时很受网络状况的影响,于是我决定改成使用本地json文件来实现。

数据的获取与之前差别不大,只需要将点击Header那步改为点击Response,就可以看到该接口的json数据,将其复制下来,格式化后贴入自己在data文件夹下新建的空白json文件中即可。

img

3.3 如何解析json数据

在解析json数据前首先要熟悉需要解析的文件结构,判断想获取的数据是存储在对象中还是数组中,甚至是嵌套着数组的对象中,需要去一层层地解析。

在解析json前需要先创建一些实体类来存放解析出来的数据。

如果目前解析的元素是对象,就用JsonObject的getAsJsonObject方法来拿到对象,用getAsString将对象某个值作为字符串存入实体类的对应变量中;如果解析的是数组,就用JsonArray的getAsJsonArray()方法来得到数组,再通过遍历数组将每一个数组元素存入实体类的list中。

eg:以players的full_name解析为例,可以看到players是一个对象数组,每个数组元素中的full_name是一个字符串

图片名称

则full_name的解析为:

    JsonArray playerArray = object.get("players").getAsJsonArray();
    List playerList = new ArrayList<>();
    for (JsonElement player : playerArray) {
        String fullName = player.getAsJsonObject().get("full_name").getAsString();
    }

4.设计实现过程

图片名称
4.1输入输出实现

​ 在AOSearch类中,我通过参数args和PrintStream对象实现了input.txt文件内容的输入和将打印结果输出到文件output.txt。创建了一个私有变量playerList来存放获取到的所有选手信息,获取方法是Lib接口中的getPlayersMap()。将input.txt的内容和playerList作为参数传入Lib接口的contentIdentify()方法中对输入内容进行分析。

4.2 对input.txt输入内容进行判断

​ 在contentIdentify()方法中创建一个list变量来存放每一行用空格分隔开的内容。如果该行第一个参数和字符串“players”一样,且该行通过空格分隔后的list只有一个元素,则该行的命令为让我们输出选手信息,调用outputPlayer()方法;如果该行第一个参数为result,且该行通过空格分隔后的list有两个元素,则调用resultIdentify()方法进入下一步对日期和赛季的判断。

​ 在resultIdentify()中,我将list元素第二个参数与“0116”······“”0129进行比对,如果与某个日期相同,调用outputDay()方法输出当日比赛结果;与”Q1“······”Q4“进行比对,如果与某个赛季相同,则调用outputQuarter()方法输出该赛季比赛结果。

4.3 对json数据进行解析并将信息存入实体类后输出

players解析:players数组->字符串uuid、short_Name、full_name、gender,对象nationality->字符串name,

创建PlayerDTO对象list,将对应信息存入;

创建MatchDTO对象list,

actual_start_time解析:matches数组->每个数组的actual_start_time,存入MatchDTO对象;

创建TeamDTO对象list,解析得到score、game、set和winner的值并存入TeamDTO对象,解析得到winner内层status的值以及team_id用于后续输出时弃赛状态的判断和比赛获胜队伍队员名字的输出。

大部分输出信息可以解析后不做改动存入实体类中直接输出,而winner的输出需要对bool值进行判断以及获胜者的名字缩写需要通过team_id找到队伍中players的uuid,然后后根据uuid找到该player对应的名字缩写,这个判断过程较为繁琐,需要频繁解析json数据,刚开始我是通过流式编程的方式,可以提高程序的简洁性。但在之后的优化中,我放弃了这种方式用map数据结构进行了优化。

PlayerDTO playerDTO = playerList.stream().filter(p -> p.getUuid().equals(playerId)).findFirst().orElse(null);

5.关键代码展示

AOSearch.java:

public static void main(String[] args) throws IOException{
        String inputPath = "input.txt";
        String outputPath = "output.txt";
        try
        {
            //使用printStream将sout的内容输出到文件
            PrintStream out = new PrintStream(outputPath);
            System.setOut(out);
        }
        catch(FileNotFoundException e)
        {
            e.printStackTrace();
        }

        Path path = Paths.get(inputPath);
        playerList=Lib.getPlayersMap();
        List<String> lines = Files.readAllLines(path);
        for (String line : lines) {
            Lib.contentIdentify(line,playerList);
        }
    }

contentIdentify()方法:

public static void contentIdentify(String line,Map<String,PlayerDTO> playerMap,List<PlayerDTO> playersList) throws IOException {
    List<String> lineList = Arrays.asList(line.split(" "));
    if (lineList.get(0).equals(PLAYER) && lineList.size() == 1) {
        outputPlayer(playersList);
    } else if (lineList.get(0).equals(RESULT) && lineList.size() == 2) {
        resultIdentify(lineList.get(1), playerMap);
    } else {
        System.out.println("Error");
        System.out.println("-----");
    }
}

getPlayersList()方法:

public static List<PlayerDTO> getPlayersList() throws IOException{
    String filePath="src\\data\\players.json";
    JsonObject object= (JsonObject) new JsonParser().parse(new FileReader(filePath));
    JsonArray playerArray = object.get("players").getAsJsonArray();
    List<PlayerDTO> playersList=new ArrayList<>();
    for (JsonElement player : playerArray) {
        String uuid = player.getAsJsonObject().get("uuid").getAsString();
        String shortName = player.getAsJsonObject().get("short_name").getAsString();
        String fullName = player.getAsJsonObject().get("full_name").getAsString();
        String gender = player.getAsJsonObject().get("gender").getAsString();
        String nationality = player.getAsJsonObject().get("nationality").getAsJsonObject().get("name").getAsString();
        playersList.add(new PlayerDTO(uuid, shortName, fullName, gender, nationality));
    }
    return playersList;
}

getMatchList()方法:

public static List<MatchDTO> getMatchList(JsonObject response, Map<String,PlayerDTO> playerMap) {
    JsonArray matches = response.get("matches").getAsJsonArray();
    Map<String,TeamPO> teamPOMap = getTeamPOList(response.get("teams").getAsJsonArray(), playerMap);
    List<MatchDTO> matchList = new ArrayList<>();


    for (JsonElement match : matches) {
        String startTime;
        if (!match.getAsJsonObject().has("actual_start_time")) {
            startTime="W/O";
            matchList.add(new MatchDTO(null,startTime));
            continue;
        }
        startTime = match.getAsJsonObject().get("actual_start_time").getAsString();
        List<TeamDTO> teamList = new ArrayList<>();
        for (JsonElement team : match.getAsJsonObject().get("teams").getAsJsonArray()) {
            List<ScoreDTO> scoreList = new ArrayList<>();
            for (JsonElement score : team.getAsJsonObject().get("score").getAsJsonArray()) {

                String game = score.getAsJsonObject().get("game").getAsString();
                String set = score.getAsJsonObject().get("set").getAsString();
                Boolean winner = score.getAsJsonObject().get("winner").getAsBoolean();
                scoreList.add(new ScoreDTO(set, game, winner));
            }

            String teamId = team.getAsJsonObject().get("team_id").getAsString();
            Boolean winner = team.getAsJsonObject().has("status");
            teamList.add(new TeamDTO(teamId, teamPOMap.get(teamId).getPlayers(), scoreList, winner));
        }
        matchList.add(new MatchDTO(teamList, startTime));
    }
    return matchList;
}

6.性能改进

​ 在项目进行的过程中,我发现对于players的数据常常需要进行多次解析,对运动员的id进行查找,而这个过程是十分耗时的!所以完成功能后我考虑用map的数据结构来对查找效率进行优化。改进的方法是:项目开始前获取playerlist,输出player时使用playerlist,保证输出顺序与接口顺序一致;获得playerlist后用该list创建playerMap,以运动员id为key,运动员实体为value,这样可以保证players.json文件只需要读取一次;在输出schedule时,解析json创建match时,需要使用运动员id查询运动员信息,利用hashmap高效的查询效率,将原本遍历查询时o(n)的时间复杂度压缩到o(logn)。利用teamid查询队伍信息时也使用map的方式,但因为队伍比较少所以优化不够明显。

​ 结果可以看到,output.txt文件中输出数据有18674行,文件大小为297KB,在使用map数据结构后,程序运行时间从1045ms缩短到407ms,缩短了一倍多。(优化前的运行时间忘记截图了呜呜)

图片名称

7.单元测试

7.1 测试代码
public class MyTest {
    @Test
    public void maintest() throws IOException {
        String inputPath = "D:\\inputtext.txt";
        String outputPath = "D:\\outputtext.txt";

        AOSearch.main(new String[]{inputPath,outputPath});
        String answerPath="D:\\rightoutput.txt";
        Path pathTest = Paths.get(answerPath);
        List<String> linesTests = Files.readAllLines(pathTest);
        Path pathResult = Paths.get(outputPath);
        List<String> linesResults = Files.readAllLines(pathResult);
        Assert.assertEquals(linesTests,linesResults);
    }
}
图片名称
7.2 覆盖率
图片名称

8.异常处理

​ 在main函数的input文件读取中或是解析json函数的json文件读取中,可能会出现无法找到文件,或是无法解析非json格式的文件等情况,通过throws IOException和catch(FileNotFoundException e)来输出异常发生时的提示。


9.心得体会

​ 一开始看到作业要求,感觉两眼一抹黑,连题目要求都看不懂。认真研读和分析半小时后才大概明白了思路,但是还是因为粗心没有看到群里助教的提示,助教已经给我们提供了解析好的json文件。我刚开始以为都要通过url请求从网络上直接获取数据,在程序功能完成之后,才发现这种方法对网络的要求很高,网络一差就出现运行超时的错误。到作业后期才改为本地json文件读取的方法,花费不少时间。所以以后我一定要及时关注软工实践课程群的消息!!但是我特别感谢助教的耐心解答和提示。在这次作业之前,我对java语言的掌握没有那么熟练,对json的结构和解析方法也只有简单的了解,同时对于单元测试Junit更是一无所知,也很少在算法类型的编程作业之外考虑过程序性能优化。而在实践之后,对java常用的对象和方法以及用Gson解析json的技术有了更深的了解和掌握,也学会了Junit的测试方法,发现程序的性能优化对编程来说是一个很重要、很有意义的部分。我认为这对我未来的项目开发将会有很大的帮助。

...全文
715 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
张书旖 2023-03-14
  • 打赏
  • 举报
回复

解题思路和设计实现过程写的很详细清晰,对问题的解决方法有自己的思考。做得很好,继续加油!

222000234张程越 学生 2023-03-23
  • 举报
回复
@张书旖 谢谢助教!
内容概要:本文系统研究了基于XGBoost的光伏阵列多类型复合故障诊断方法,深入探讨了如何利用XGBoost这一高性能集成学习算法实现对光伏系统中短路、开路、阴影遮挡等多种典型故障的高精度识别与分类。文章详细阐述了XGBoost模型的理论基础及其在故障诊断中的独特优势,包括优异的非线性拟合能力、对缺失数据的鲁棒性、高效的训练速度以及出色的泛化性能。研究通过采集光伏阵列的实际运行数据(如电压、电流、温度等关键特征参数),构建高质量的训练数据集,并设计合理的特征工程流程,进而建立基于XGBoost的故障诊断模型。实验结果表明,该模型在准确率、召回率和F1-score等核心评价指标上均显著优于传统的SVM、决策树等机器学习方法,展现出卓越的诊断效能。此外,文章配套提供了完整的Python代码实现,涵盖数据预处理、模型训练、参数调优与性能评估全过程,极大地方便了读者复现研究成果并将其应用于实际的光伏电站智能运维系统中。; 适合人群:具备一定Python编程能力和机器学习基础知识,从事新能源发电、电力系统监控、智能运维或工业故障诊断等相关领域的科研人员、工程师和技术开发者,特别适合致力于提升光伏发电系统自动化、智能化管理水平的专业人士。; 使用场景及目标:① 实现光伏电站的实时故障监测与早期预警,提升运维效率并降低发电损失;② 构建智能诊断模块集成于光伏监控平台,实现故障的自动化识别与分类;③ 为相关科研项目提供可复现的算法范例与代码基础,推动先进机器学习技术在新能源领域的应用研究; 阅读建议:建议读者结合所提供的完整Python代码,动手实践数据清洗、特征提取、模型训练与交叉验证的全流程,深入理解XGBoost算法的关键参数(如学习率、树的深度、正则化项)对模型性能的影响,并尝试通过网格搜索或贝叶斯优化等方法进行超参数调优,以适应不同规模和环境下的光伏系统诊断需求。
内容概要:本文提出了一种针对分布式光伏储能系统的优化配置方法,并基于Matlab实现相关代码仿真。研究聚焦于多渗透率电动汽车接入背景下配电网承载能力的评估问题,构建了一个涵盖一次设备安全、负荷平稳性、电能质量和系统效率等多维度的综合评价指标体系。通过融合熵权法与模糊综合评价方法,建立了双层评分模型,实现了对不同场景下配电网承载能力的客观量化评估。文中设计了多场景算例进行仿真分析,系统探讨了电动汽车不同渗透率对配电网各项性能指标的影响规律,并开展了指标灵敏度分析,充分验证了所提方法在复杂工况下的有效性与实用价值。; 适合人群:具备电力系统基础知识,熟悉Matlab编程,从事新能源并网、智能电网规划与优化等相关领域的研究人员及工程技术人员,尤其适合研究生及以上学历或具有1年以上相关工作经验的专业人员; 使用场景及目标:①用于评估高比例分布式光伏与电动汽车接入对配电网承载能力的影响;②为配电网扩容改造、储能系统配置及电动汽车充电站规划提供科学决策支持;③作为科研项目或学位论文的复现与扩展研究基础; 阅读建议:建议读者按照文档目录顺序系统学习,重点关注模型构建逻辑与Matlab代码实现细节,结合提供的仿真结果深入理解评价方法的应用流程,有条件者应动手复现代码以加深掌握。

688

社区成员

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

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