688
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2023春季软件工程&实践w班 |
|---|---|
| 这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
| 这个作业的目标 | 实现程序的基本功能和附加功能 单元测试和性能分析 错误处理 |
| 其他参考文献 | 《构建之法》 |
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 20 | 25 |
| • Estimate | • 估计这个任务需要多少时间 | 20 | 25 |
| Development | 开发 | 700 | 794 |
| • Analysis | • 需求分析 (包括学习新技术) | 90 | 100 |
| • Design Spec | • 生成设计文档 | 30 | 35 |
| • Design Review | • 设计复审 | 20 | 30 |
| • Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 60 | 44 |
| • Design | • 具体设计 | 40 | 55 |
| • Coding | • 具体编码 | 300 | 320 |
| • Code Review | • 代码复审 | 40 | 60 |
| • Test | • 测试(自我测试,修改代码,提交修改) | 120 | 150 |
| Reporting | 报告 | 75 | 105 |
| • Test Repor | • 测试报告 | 30 | 40 |
| • Size Measurement | • 计算工作量 | 15 | 15 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 50 |
| 合计 | 795 | 924 |
拿到题目后,我首先对作业的要求进行了一个粗略的概览,最后我将本次作业的要点分为:
1.准备数据->如何获取未提供的JSON文件?(利用开发者工具在网页直接获取)
2.读写文件->程序将从input.txt中读取指令,并输出到output.txt中
3.解析JSON->程序将借助GSON库对JSON文件进行解析,从中提取所需要的信息
eg:
players(JsonArray)->full_name(JsonObject)
gender(JsonObject)
nationality(JsonObject)->name(JsonObject)
4.条件判断->程序能够对不同指令做出对应的处理,包括正确的指令、错误的指令以及多行指令
我按照我所分类的作业要点,结合作业要求,依次达到本作业不同方面的要求,过程中我所查找的资料都是根据我不熟练的点来搜索。
例如,在程序需求中,我进行了有关Java读写文件、GSON解析JSON文件、idea如何打包jar等方面的资料查找;
而在单元测试时,我进行了Java如何编写单元测试的查找。
首先,我对该程序所用到的资源文件,即囊括信息的JSON文件进行了拆解,结合程序所产生的内容,决定只用一个主类(即AOSearch.java)来完成整个程序。这样做的理由是,如果通过创建类的方式来获取JsonObject,会产生许多用不到的字段的浪费,例如,我一开始想尝试只定义程序需要的内容相关的变量,但是这样无法解析类来获得对象,我又搜索了其他的相关资料,发现都是将整个JSON文件的变量写全的(也可能因为我经验不足没找到只获取相关字段的写法),我认为这样没有必要,所以直接采用逐层解析JSON文件的方法。
因此,在再次分析程序的需求之后,我将该程序所需要的函数划分为:
①读取input.txt文件的内容
②将字符串写入output.txt
③读取所有选手信息
④读取正式赛和资格赛信息
⑤读取jar包资源文件内容
接着,我根据这五个需求创建了五个函数,分别是
readFile、writeFile、readPlayers、readOfficialCompetition、readJsonFileInJar
这些函数都将在主类AOSearch.java中实现和调用。而这些函数之间的关系如下图:

getAsJsonArray()、JsonPaser.parse(String)、get(String).getAsJsonObject()、get(String).getAsJsonObject()等关键函数获取我所需要的信息。JsonPrimitive类之外,并没有在技术上有其他改变。 String nameOfFirstPlayer = "";
String nameOfSecondPlayer = "";
int i2 = 0;
for (Object o6 : playersInTeams) {
i2++;
JsonPrimitive playerInTeams = (JsonPrimitive) o6;
...
for (Object o7 : firstPlayers) {
...
if (playerUuidInTeams.equals(puuid)) {
String short_name = firstPlayer.get("short_name").getAsString();
if (i2 == 1) {
nameOfFirstPlayer = short_name;
}
if (i2 == 2) {
nameOfSecondPlayer = short_name;
}
}
}
}
if (!nameOfSecondPlayer.equals("")) {
data += "winner:" + nameOfFirstPlayer + " & " + nameOfSecondPlayer + "\n";
} else {
data += "winner:" + nameOfFirstPlayer + "\n";
}
该段代码是为了应对团队赛和单人赛的不同情况写的,与JSON文件中有时不存在的actual_start_time和status是相同的原理,因此此处只放出这段比较代表性的代码。但由于该信息是同一个playersJSONArray中不同size的情况,因此还用到了JsonPrimitive抽象类的转换。如果不对这些情况进行提前判断,就会抛出空指针异常(Null Pointer Exception)。
String[] RightDay = {"Q1", "Q2", "Q3", "Q4", "0116", "0117", "0118", "0119", "0120", "0121", "0122",
"0123", "0124", "0125", "0126", "0127", "0128", "0129"};
String[] datas;
datas = data.split("\\r?\\n");
int i = 0;
while (i != datas.length) {
String dataWithoutBlank = datas[i].trim() ;
if (dataWithoutBlank.equals("players")) {
...
} else if (dataWithoutBlank.startsWith("result ")) {
String str = dataWithoutBlank.replaceAll(" ","");
String resultStr = str.substring(6);
for (String rd : RightDay) {
if (rd.equals(resultStr)) {
isRightDate = true;
outPutData += readOfficialCompetition(rd);
break;
}
}
if (!isRightDate) {
outPutData += "N/A\n" + "-----\n";
}
isRightDate = false;
}
这是我为读取文件写的一段代码,其中datas = data.split("\\r?\\n");是按换行符分割读取的字符串,从而逐一判断输入的多行指令。而String[] RightDay则是负责判断所需资格赛和正式赛的日期以及方便调用输出信息的函数。
BufferedReader in = new BufferedReader(new InputStreamReader(AOSearch.class.getClassLoader().getResourceAsStream(filename)));
if (in == null) {
System.out.println("null");
return "null";
} else {
StringBuffer buffer = new StringBuffer();
String line = "";
while (true) {
try {
if (!((line = in.readLine()) != null)) break;
} catch (IOException e) {
throw new RuntimeException(e);
}
buffer.append(line);
}
String input = String.valueOf(buffer);
return input;
}
这段代码是用于以流的形式获取jar包资源文件的内容,因此在整个程序中,该函数的调用返回值是整段JSON字符串。由于jar包文件路径是临时存在的,因此不能用普通的读取相对路径的方式对文件进行读取。
①players
②"result "+正确格式
③"result "+错误格式 --->"N/A"
④空行 --->跳过
⑤其他 --->"Error"
(后经补充还有单独的“result”也输出“N/A”的情况)
其中②③是比较繁琐的判断情况,因为资格赛和正式赛的日期是有十几种情况的,所以肯定不能用枚举的方法来进行if判断,这样会产生许多相似代码片段,也降低了可读性。因此我将readOfficialCompetition函数新增传入了参数,这样在调用时可以直接通过result 后的日期来获取对应信息。但是判断还是有很多种情况,所以我新建了一个String数组来存放所有正确日期,并用for循环与获取的指令进行对比。
HashMap存储方法。
由于本人先前没有单元测试的习惯,因此在这次作业中也是第一次对程序进行单元测试。我在对网上的资料进行观看后决定直接用简单的断言判断输出方法。由于我事先没有经验,因此我复用了原本写的代码,写了一个新的函数专门用于测试,只调用了输出函数,导致覆盖率非常之低,由此我认为提高覆盖率的方法,其中很重要的一点就是尽量分散函数的功能,这样在测试时才能调用到程序大部分的函数。
这是我编写的其中一个测试函数的部分代码,是用来判断错误情况的↓

我编测试的思路是:通过指定的输入指令来判断输出是否正确。但同时因为测试简单,覆盖率也不高,接下来的几个测试示例与第一个大同小异,代码就不展示了,直接放覆盖率。

cmd输入的指令为Java -jar AOSearch.jar input.txt output.txt
因此我专门根据该条指令分析了可能的异常及对应措施。
已知Java -jar AOSearch.jar是运行jar包的基本格式指令,因此在输入不正确时,cmd会自动报错。
而读取网球公开赛的选手信息和比赛结果的指令输入在用户自己创建的input.txt中,因此如果用户没有创建input.txt,cmd都会输出创建文件的要求来提醒用户。因为output.txt由程序自己创建,所以不用用户另行设置。
条理清晰,结构清楚,赞!