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

222200115吴俊斌 2024-09-10 23:24:42
这个作业属于哪个课程软件工程实践-2024学年-W班
这个作业要求在哪里软件工程实践第二次作业——个人实战
这个作业的目标收集数据、编写代码、单元测试、性能改进、异常处理、撰写博客
其他参考文献单元测试与回归测试源代码管理工程师的能力评估和发展

目录

一、项目地址

CodeArts地址

二、PSP表格

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

三、解题思路描述

3.1 需要的数据文件

在官方网站打开开发者工具,找到以下两份可格式化的json文件。

  1. medals.json
  2. medalists.json
    该网站(JSON在线)将其格式化。

解析后替换为更利于文件流读取与分析的txt文件。
4. Nationtotal.txt 用于实现功能1
5. ResultDate.txt 用于实现功能2
6. NationInfo.txt 用于实现功能3

3.2 需要的功能函数

  1. total 则会输出所有国家得奖情况。
rank1:string
gold:number
silver:number
bronze:number
total:number
-----
  1. result 0810(date) 则会输出8月10日的所有人员获奖情况。
winner:string
nationality:string 
gold:number
silver:number
bronze:number
total:number
-----
  1. PK 中国(countryname) 美利坚合众国(countryname) 霹雳舞(eventDescription) 则会输出2024年巴黎奥运会霹雳舞项目中国和美利坚合众国的奖牌情况。
stringVSstring string
gold:number/number
silver:number/number
bronze:number/number
total:number/number
-----

4.出现无法识别的指令,则输出Error;
result后的日期不在限定时间内(7月27日-8月11日),则输出N/A。

3.3 需要的I/O函数

  1. 通过命令行指令传入文件。
Java -jar OlympicSearch.jar input.txt output.txt
  1. 读取input中的指令:使用Files.readAllLines逐行遍历input.txt的指令,并存储到CommandList中。
  2. 根据指令List获取对应数据:首先获取CommandList,然后遍历并辨别指令,根据指令获取对应的数据,调用相应的功能函数。
  3. 将数据写入output文件:获得对应数据后通过BufferedWriter写入文件中。

四、接口设计与实现过程

src
 │  OlympicSearch.java     # 主程序所在模块,读命令,调用函数
        public static List<String> readCommands(String input):读取文件中的命令
        public static void main(String[] args) : 入口函数,调用不同的功能函数
        
 │  NationTotal.java     # 响应total指令
        public static List<CountryMedal> readMedalData(String filePath) :获取NationTotal文件中的国家奖牌数据,转换成对象
        public static List<CountryMedal> sortByMedals(List<CountryMedal> countries)  :按奖牌数量逐级降序排列
        public static void writeOutput(List<CountryMedal> countries, BufferedWriter writer) : 获得最终的数据并写入output
        
│     ResultDate.java     # 响应result date指令
        public static Map<String, List<WinnerMedal>>readAllResults(String filePath):// 读取ResultDate文件,获得所有对象,按日期分组
        public static void ResultCommand(String date, String ResultDateFilePath, BufferedWriter writer) : // 处理主函数传来的ResultDate指令
        public static List<WinnerMedal> sortByMedals(List<WinnerMedal> winners):按奖牌数量逐级降序排列
        public static void writeOutput(List<WinnerMedal> winners, BufferedWriter writer): 获得最终的数据并写入output
        
│   NationPK.java     # 响应pk指令
        public static Map<String, Map<String, int[]>> readCountryPKData(String filePath): // 读取 NationInfo 文件,返回 <比赛项目,<国家,奖牌数[]>> 的数据结构,便于储存与比较
        public static void CountryPK(String country1, String country2, String project, String filePath, BufferedWriter writer) : 获取并PK两个国家在某个项目上的奖牌数,写入output

 ├─entity
         CountryMedal .java              # 国家-奖牌类    
         WinnerMedal.java           # 运动员-奖牌类

五、关键代码展示

5.1 读取并辨识指令,流程的入口函数

        try {
            // 读取指令
            List<String> commands = readCommands(inputFilePath);

            // 创建BufferedWriter,写入的内容将覆盖原output.txt
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath,false))) {
                for (String command : commands) {
                    // 去除单行首尾空格
                    command = command.trim();

                    // 若指令可以满足NationTotal的1个参数
                    if ("total".equalsIgnoreCase(command)) {
                        //调用NationTotal
                        List<CountryMedal> countries = NationTotal.readMedalData(NationTotalFilePath);
                        List<CountryMedal> sortedCountries = NationTotal.sortByMedals(countries);
                        NationTotal.writeOutput(sortedCountries, writer);

                    }

                    // 若指令可以满足ResultDate的2个参数
                    else if (command.startsWith("result")) {
                        //根据空格数拆分part
                        String[] parts = command.split("\\s+");
                        if (parts.length >= 2) {
                            String date = parts[1].trim();
                            // 调用ResultDate
                            ResultDate.processResultCommand(date, ResultDateFilePath, writer);
                            // 处理错误参数
                        } else {
                            writer.write("Error\n");
                            writer.write("-----\n");
                        }

                        // 若指令可以满足NationPK的4个参数
                    }  else if (command.startsWith("PK")) {
                        //根据空格数拆分part
                        String[] parts = command.split("\\s+");
                        if (parts.length >= 4) {
                            String country1 = parts[1].trim();
                            String country2 = parts[2].trim();
                            String project = parts[3].trim();
                            NationPK.processComparison(country1, country2, project, countryVsFilePath, writer);
                        } else {
                            writer.write("Error\n");
                            writer.write("-----\n");
                        }
                    // 错误指令
                    } else {
                        writer.write("Error\n");
                        writer.write("-----\n");
                    }
                }
            }

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

5.2 处理ResultDate指令

    public static void processResultCommand(String date, String ResultDateFilePath, BufferedWriter writer) {
        try {
            // 读取所有对象及其对应日期
            Map<String, List<WinnerMedal>> allResults = readAllResults(ResultDateFilePath);

            // 转换日期格式,如 "0809" => "8.9"
            String month = date.substring(0, 2).replaceFirst("^0+", "");
            String day = date.substring(2, 4).replaceFirst("^0+", "");
            String formattedDate = month + "." + day;

            // 获取指定日期的对象
            if (allResults.containsKey(formattedDate)) {
                List<WinnerMedal> winners = allResults.get(formattedDate);

                // 根据金牌、银牌、铜牌排序
                List<WinnerMedal> sortedWinners = sortByMedals(winners);

                // 输出结果到 BufferedWriter
                writeOutput(sortedWinners, writer);
            } else {
                // writer.write("未找到指定日期的比赛结果:" + formattedDate + "\n");
                writer.write("N/A" +  "\n");
                writer.write("-----\n");
            }

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

    // 按照运动员获得的奖牌数量,逐级降序排列
    public static List<WinnerMedal> sortByMedals(List<WinnerMedal> winners) {
        return winners.stream()
                .sorted(Comparator.comparingInt((WinnerMedal  w) -> w.gold)
                        .thenComparingInt(w -> w.silver)
                        .thenComparingInt(w -> w.bronze).reversed())
                .collect(Collectors.toList());
    }

5.3 处理CountryPK指令

   public static void CountryPK(String country1, String country2, String project, String NationInfoFilePath, BufferedWriter writer) {
        try {
            // 读取 NationInfo 文件
            Map<String, Map<String, int[]>> NationInfoData = readCountryPKData(NationInfoFilePath);

            if (NationInfoData.containsKey(project)) {      //包含指令要求的项目
                Map<String, int[]> countryData = NationInfoData.get(project);

                // 在该项目中寻找这两个国家的奖牌数据,如果没有则默认奖牌数为0
                int[] country1Data = countryData.getOrDefault(country1, new int[]{0, 0, 0, 0});
                int[] country2Data = countryData.getOrDefault(country2, new int[]{0, 0, 0, 0});

                // 将PK结果写入 BufferedWriter
                writer.write(country1 + " VS " + country2 + " " + project + "\n");
                writer.write("gold: " + country1Data[0] + " / " + country2Data[0] + "\n");
                writer.write("silver: " + country1Data[1] + " / " + country2Data[1] + "\n");
                writer.write("bronze: " + country1Data[2] + " / " + country2Data[2] + "\n");
                writer.write("total: " + country1Data[3] + " / " + country2Data[3] + "\n");
                writer.write("-----\n");
            } else {
                writer.write("N/A" + "\n");
                writer.write("-----\n");
            }

        } catch (IOException e) {
            try {
                //writer.write("Error occurred during comparison\n");
                writer.write("Error\n");
                writer.write("-----\n");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }

六、性能改进

  1. 使用StringBuffer 处理字符串

    使用StringBuffer拼接字符串不会产生新对象,减少频繁创建对象,配合BufferedWriter的缓冲功能,可以提高读写效率。

  2. 使用HashMap缓存数据

    当读取一组关系对象时可存入HashMap中,可以减少读取数据文件的时间。

  3. 将指令用List存储

    将input中的内容一次性读取出来用List<String>存储,可以减少读取指令文件的时间。

七、单元测试

以下包含10+各测试用例。

功能1测试(input-output)

功能1测试imput

功能1测试output

功能2测试(input-output)

功能2input


功能2output

功能3测试(input-output)

功能3input


功能3output

八、异常处理

读取指令可能存在异常,因此使用try-catch语句来包含文件读取语句。

尝试读取文件

当指令参数异常时,应作业要求,不选择try报错处理,而是在output文件中打印“Error”字样。

处理错误参数

当日期出现非法输入时,应作业要求,不选择try报错处理,而是在output文件中打印“N/A”字样。

处理错误日期

PK功能可能出现的异常状态很多,根据类型选用"Error"或"N/A"或e.printStackTrace()反馈异常。

PK异常

九、心得体会

这次实践涉及的技术多且杂,包括网站数据的爬取与解析,文件格式化与读写,数据的存储与处理,异常处理,单元测试,Git的使用等等,会遇到很多很多的感到一时无语,束手无策的困难。但在同学的帮助与自己的查找下解决了大部分的困难。经过这次实践,我更深刻地理解到了软件工程领域的浩瀚无边,大一大二时所忙碌的编写代码环节,在一个工程中所耗费的时间与精力竟然也并非重点。查找与学习永无止境。

...全文
258 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
助教袁金凡 助教 2024-10-16
  • 打赏
  • 举报
回复

你的博客文档标题的跳转地址似乎错了

内容概要:本文围绕基于分布鲁棒优化的风电不确定性机组组合问题展开研究,提出了一种能够有效应对风电出力不确定性的数学模型与求解方法。通过构建分布鲁棒优化模型,充分考虑风电预测误差的概率分布特征及其不确定性集合的数学表达,在保障电力系统安全运行的前提下,优化发电机组的启停计划与出力分配,从而提升调度方案的经济性与鲁棒性。文中系统阐述了模型的理论基础、不确定性集合的构造方式、目标函数与约束条件的设计,并结合Matlab代码实现了算法仿真与案例验证,展示了该方法在不同场景下的适应能力与性能优势。; 适合人群:具备电力系统分析、优化理论基础及Matlab编程能力,从事新能源并网调度、电力系统运行优化、能源系统建模等方向的研究人员、高校研究生以及相关领域的工程技术开发者。; 使用场景及目标:①应用于高比例可再生能源接入背景下的电力系统机组组合决策,增强调度方案对风电波动的抵御能力;②为电力市场环境中的发电计划编制与日前调度提供科学依据和技术支持;③作为分布鲁棒优化理论在能源系统不确定性建模中应用的教学与科研案例,推动相关算法的推广与改进。; 阅读建议:建议读者结合Matlab代码深入理解模型构建与求解流程,重点关注不确定性集的设定逻辑、鲁棒性参数的选择以及算法实现细节,可进一步拓展至多能源耦合系统或多时间尺度调度场景的研究。
内容概要:本文提出了一种结合蒙特卡洛模拟与拉格朗日松弛法的分散式优化方法,用于解决电动汽车充电站在分时电价机制下的有序充电调度问题。通过蒙特卡洛方法模拟电动汽车充电行为的随机性,生成多场景充电负荷数据,并构建以降低电网负荷波动和用户充电成本为目标的优化模型。采用拉格朗日松弛法对问题进行解耦,实现各电动汽车在保护用户隐私的前提下自主参与调度决策,有效提升了计算效率与实际应用可行性。仿真实验验证了该方法在削峰填谷、减少用户电费支出以及增强电网运行稳定方面的优越性能; 适合人群:具备电力系统优化、智能交通系统、运筹学或能源管理等相关背景的研究生、科研人员及工程技术人员; 使用场景及目标:①应用于城市规模化充电站集群的分散式调度系统设计与优化;②研究分时电价政策下用户充电行为与电网负荷之间的互动响应关系;③为高比例电动汽车接入场景下的电网协同控制提供算法支持与仿真验证平台; 阅读建议:读者应结合提供的Matlab代码,深入理解蒙特卡洛场景生成与拉格朗日松弛算法的实现逻辑,建议在复现过程中调整电价策略、用户数量等关键参数,观察其对调度效果的影响,并可进一步拓展至多目标优化框架或引入可再生能源出力不确定性进行联合建模研究。

239

社区成员

发帖
与我相关
我的任务
社区管理员
  • FZU_SE_teacherW
  • 202501福大-软件工程实践-W班
  • D's Honey
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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