这个作业属于哪个课程 | 2302软件工程 |
---|---|
这个作业要求在哪里 | 软件工程第二次作业–文件读取 |
这个作业的目标 | 完成对世界游泳锦标赛跳水项目相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序 |
其他参考文献 | CSDN、《构建之法》、单元测试和回归测试 |
文章目录
1.Gitcode项目地址
仓库地址: https://gitcode.net/qq_63574466/project-java
2. PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1320 | 1540 |
• Analysis | • 需求分析 (包括学习新技术) | 500 | 540 |
• Design Spec | • 生成设计文档 | 60 | 40 |
• Design Review | • 设计复审 | 60 | 40 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
• Design | • 具体设计 | 80 | 180 |
• Coding | • 具体编码 | 300 | 360 |
• Code Review | • 代码复审 | 60 | 50 |
• Test | • 测试(自我测试,修改代码,提交修改) | 240 | 300 |
Reporting | 报告 | 110 | 105 |
• Test Repor | • 测试报告 | 60 | 60 |
• Size Measurement | • 计算工作量 | 20 | 15 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1460 | 1675 |
3.解题思路描述
1.Json数据的提取
在尝试网站提取后,最终决定使用来源于example/data文件夹内的Json数据
2.对Json数据解析
打开Json文件,查看需要提取的数据位置,
了解了json文件的树形结构,并知道了需要提取的数据所在位置,之后代码就可以按照编写。
通过修改results下比赛成绩Json文件的名称,可以在输入比赛项目时,直接通过比赛项目的名称进行查找。
文件名称的修改如下图所示:
women_10m_synchronised.json
这样的修改大大方便了json文件的查找
3. 代码组织思路
提取完成Json文件,并确定了Json文件的结构后,只需要根据输入的命令,从Json文件中提取出需要的数据,再进行输出即可。
- 为此需要解析Json文件的工具类和文件操作的工具类。
- 同时运动员和比赛成绩的两个实体类也是不可缺少的。
- 为了能够满足输出格式,也需要输出远动员信息以及比赛成绩信息的关键函数。
4.设计与实现过程
1.代码的组织
首先确认了代码中包含2个实体类Player和Result、两个工具类FileUtil和JasonParseUtil、以及三个核心功能类GetJsonElement、PrintPlayers和PrintMatchResults。
- JasonParseUtil中使用了谷歌的gson去解析json数据。
- PrintPlayers和PrintMatchResults中的两个静态方法printPlayers和printMatchResults,分别是向输出文件中输出选手信息和比赛成绩。
- GetJsonElement使用了Apache Commons IO的第三方库,使IO操作的代码变得更简单倾斜。
2.关键代码展示
GetJsonElement类,核心是getRootJasonElement方法,目的是为了从文件中获取根JasonElement
在代码中使用了Apache Commons IO的第三方库,与手动编写这些功能相比,实现方法的代码变得更小,更清晰,更易理解,大大方便了编程。
//从指定的文件中获得根JasonElement
public JsonElement getRootJasonElement(String inputFile) {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(inputFile);
JsonElement jsonElement = null;
jsonElement = readFileToJE(inputStream);
return jsonElement;
}
//把指定的文件读取成JsonElement
private JsonElement readFileToJE(InputStream inputStream){
JsonElement jsonElement = null;
if (inputStream != null) {
String jsonString = readFileToString(inputStream);
if (jsonString == null){
System.out.println("读取数据文件出错!");
return jsonElement;
}
jsonElement = JsonParser.parseString(jsonString);
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
} else {
return null;
}
return jsonElement;
}
//把指定的文件读取成JsonElement
private String readFileToString(InputStream inputStream){
String jsonString = null;
try {
jsonString =IOUtils.toString(inputStream, StandardCharsets.UTF_8);
}catch (IOException e){
System.out.println("读取数据文件出错!");
}
return jsonString;
}
PrintMatchResults类,主要目的是向文件中输出比赛成绩
//向文件输出所有运动员的信息
public static void printResults(String outputFile,String command){
String event = command.toLowerCase().replaceFirst("result ", "").replace(" ", "_");
String classPath = "data/results/" + event + ".json";
GetJsonElement getJasonElement = new GetJsonElement();
JsonElement jsonElement = getJasonElement.getRootJasonElement(classPath);
List<Result> results = JsonParseUtil.parseFinalResult(jsonElement);
FileUtil.append(outputFile,ResultListToString(results));
}
//将比赛成绩列表转化为字符串
private static String ResultListToString(List<Result> results) {
if (results == null || results.isEmpty()) {
return "N/A\n-----";
}
StringBuilder builder = new StringBuilder();
for (Result r : results) {
builder.append(finalResult(r)).append("-----\n");
}
return builder.toString();
}
//将比赛成绩单个对象转变为字符串
private static String finalResult(Result r){
String finalScore =null;
List<String> score = r.getFinalScore();
if (score == null) {
finalScore = "*\n";
return
"Full Name:" + r.getFullName() + "\n" +
"Rank:" + r.getRank() + "\n" +
"Score=" + finalScore;
}
BigDecimal totalScore = score.stream()
.map(BigDecimal::new)
.reduce(BigDecimal.ZERO, BigDecimal::add);
finalScore = score.stream()
.collect(Collectors.joining(" + ", "", " = " + totalScore + "\n"));
return
"Full Name:" + r.getFullName() + "\n" +
"Rank:" + r.getRank() + "\n" +
"Score=" + finalScore;
}
5.程序性能改进
5.1 对性能改进的分析
I/O流的重复操作会使代码重复率变高,同时也会影响程序的性能。输入流用FileReader会比较慢。
5.2 对性能的具体改进
使用BufferedReader去对输入流进行控制
List<String> lines = new ArrayList<>();
try (InputStream is = new FileInputStream(file)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} catch (IOException e) {
System.err.println("文件不存在或无法读取: " + e.getMessage());
}
6.单元测试展示
测试采用java的Juit进行测试
对DWASearch.java的单元测试(主要对参数不同,与参数错误进行测试)
@org.junit.jupiter.api.Test
void test1() {
//错误的参数输入
DWASearch.main(new String[]{"input"});
}
@org.junit.jupiter.api.Test
void test2() {
//input.txt内命令格式错误
DWASearch.main(new String[]{"input.txt"});
}
@org.junit.jupiter.api.Test
void test3() {
//正确的只含players命令的input1.txt和output.txt
DWASearch.main(new String[]{"input1.txt", "output.txt"});
}
@org.junit.jupiter.api.Test
void test4() {
//正确的只含players命令的input1.txt和output
//output没有后缀但可正常写入
DWASearch.main(new String[]{"input1.txt","output"});
}
@org.junit.jupiter.api.Test
void test5() {
//正确的只含players命令的input1.txt和output.6
//output后缀为6但可正常写入
DWASearch.main(new String[]{"input1.txt","output.6"});
}
@org.junit.jupiter.api.Test
void test6() {
//正确的只含result women 1m springboard命令的input1.txt和output.txt
DWASearch.main(new String[]{"input2.txt","output.txt"});
}
@org.junit.jupiter.api.Test
void test7() {
//正确的含players和result women 1m springboard双命令的input1.txt和output.txt
DWASearch.main(new String[]{"input3.txt","output.txt"});
}
测试结果
1、对test1的测试
2.对test2的测试
3.对test3的测试
4.对test4的测试
5.对test5的测试
6.对test6的测试
7.对test7的测试
与预期相符
对接口函数的测试(主要对,各种不一样参数的输入进行判断)
@org.junit.jupiter.api.Test
void test_1() {
//输出到output.txt
PrintPlayers.printPlayers("output.txt");
}
@org.junit.jupiter.api.Test
void test_2() {
//错误的play命令,输出到output2.txt
PrintMatchResults.printResults("output.txt2","play");
}
@org.junit.jupiter.api.Test
void test_3() {
//正确的players命令,输出到output3.txt
PrintMatchResults.printResults("output.txt3","players");
}
测试结果
1.对test_1的测试
2.对test_2的测试
3.对test_3的测试
与预期相符
思考如何优化覆盖率?
1.标识未覆盖的代码: 使用代码覆盖工具来分析测试用例,并标识哪些代码路径没有被覆盖到。这可以帮助专注于测试覆盖率较低的区域。
3.增加边界测试: 在测试用例中添加更多的边界条件,以确保代码在极端情况下仍然能够正确运行。
4.模拟异常情况: 确保测试用例包括对异常情况的测试,以验证代码在出现错误时的行为。
追踪分支覆盖率: 关注和追踪代码中的分支语句,确保每个分支都被测试到。这可以通过代码覆盖工具提供的分支覆盖率报告来实现。
7.心路历程与收获
我认为这次作业主要的难点在于对于json数据的解析、单元测试的编写、性能的提升、还有打包文件的过程。
尤其是打包时,因为使用了第三方库,所以打包时遇到了很多问题,在网上花费大量时间进行了很多搜索后,才找到具体的解决办法。
分析题目不难,但真正写起来时,却在Json数据的提取,以及文件读取检测、文件输入方面有很多不懂的地方。在网上的大量学习和请教下,终于完成了任务。