这个作业属于哪个课程 | <软件工程-23年春季学期> |
---|---|
这个作业要求在哪里 | <软件工程实践第二次作业—文件读取> |
这个作业的目标 | <完成对澳大利亚网球公开赛相关数据的收集,并实现一个能够对赛事数据进行统计的控制台程序> |
其他参考文献 | 《构建之法》《源代码管理》 |
目录:
0.Gitcode项目地址
- PSP表格
-
解题思路描述
- 从相关网址获取json
- json解析
- 数据提取分析
-
接口设计和实现过程
- 接口设计
- 接口实现
- 关键代码展示
-
性能改进
- 分析
- 改进
- 单元测试
- 异常处理
- 心得体会
0.Gitcode项目地址
仓库地址
1. PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
• Estimate | • 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 600 | 500 |
• Analysis | • 需求分析(包括学习新技术) | 120 | 100 |
• Design Spec | • 生成设计文档 | 20 | 50 |
• Design Review | • 设计复审 | 60 | 60 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
• Design | • 具体设计 | 120 | 60 |
• Coding | • 具体编码 | 360 | 290 |
• Code Review | • 代码复审 | 120 | 90 |
• Test | • 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 210 | 165 |
• Test Repor | • 测试报告 | 80 | 60 |
• Size Measurement | • 计算工作量 | 60 | 60 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 1970 | 1665 |
2. 解题思路描述
2. 1 从相关网址获取json
1、
2、
3、
4、
2.2 json数据解析
用vs打开压缩过的json数据,然后点击格式化
players.json的数据可以看成上面这几个部分,而我们主要需要提取的是players这一项
点开players这一项
查看
发现
需要的数据都在里面
构成json-》players-》full_name / nationality->name /gender的树形结构,代码即可从这里入手
接着分析result date的json数据
总体分为
这样的一个结构
我们需要的数据在
点开matchs
看到我们需要的数据[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
2.3、数据提取分析
根据需求我们需要获得winner的名字,但是根据json文件我们只能获得team_id
如图
所以需要查看该文件内teams数组内的内容
如图
再由玩家hash值去该文件的players去查找
如图
代码编写也是按照如上思路去进行
3. 接口设计和实现过程
3.1接口设计
由需求可知需要将打印全部玩家与打印全部比赛结果提取出来,所以考虑使用java8的新属性,可在接口中使用静态方法,继承者可以直接调用接口中的静态方法
- 第一步需要实现一个静态的writePlayers的方法,内部用阿里巴巴的fastjson去解析json数据,并且根据题目要求打印
- 第二部需要实现静态的writeResults的方法,需要实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称
- 第三步提取输入内容的过程实现writeInfo函数,用于随时向output.txt输入内容
3.2实现过程
-
由上述分析可知,需要先导入阿里巴巴的fastjson解析包,调用fastjson的jsonObject与jsonArray去解析对应数据
-
然后调用内部api实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称的关键代码
-
writePlayers函数与writeResults函数只需要调用对应api即可
4. 关键代码展示
- 实现searchTeam与searchWinner的方法,去查找胜利的队伍内对应玩家的简称的关键代码
//通过uuid查找队伍
static JSONObject searchTeam(String teamUuid,JSONArray teams){
for (int i = 0;i < teams.size();i++){
if (teams.getJSONObject(i).getString("uuid").equals(teamUuid)){
return teams.getJSONObject(i);
}
}
return null;
}
//通过uuid数组查找选手
static JSONArray searchWinners(String playerUuids,JSONArray players){
JSONArray winners = new JSONArray();
JSONArray uuids = JSONArray.parseArray(playerUuids);//需要查找的选手uuid
for (int i = 0; i < uuids.size(); i++){
String uuid = uuids.getString(i);
for (int j = 0; j < players.size(); j++){
if (players.getJSONObject(j).getString("uuid").equals(uuid)){
winners.add(players.getJSONObject(j));
}
}
}
return winners;
}
- 调用api实现打印全部选手的函数、
//对应接口中的writePlayers代码如下
static void writePlayers(String outputFile) throws Exception {
if (cache.containsKey("players")) {
// 重复请求
writeInfo(cache.get("players"),outputFile);
return;
}
JSONObject jsonObject = JSONObject.parseObject(getJSONStr(PLAYERS_FILES));
JSONArray players = JSONArray.parseArray(jsonObject.getString("players"));//获取运动员json数组
StringBuilder playersInfo = new StringBuilder();//遍历运动员数组,把相关信息加入文件输出
for (int i = 0; i < players.size(); i++){
JSONObject player = players.getJSONObject(i);
playersInfo.append("full_name:" + player.getString("full_name") + "\n");
playersInfo.append("gender:" + ("F".equals(player.getString("gender"))? "Female" : "Male") + "\n");
playersInfo.append("nationality:" + player.getJSONObject("nationality").getString("name") + "\n");
playersInfo.append("-----\n");
}
cache.put("players",playersInfo.toString());//第一次读取的时候
writeInfo(playersInfo.toString(),outputFile);
}
5. 性能改进
5.1 分析
- 性能的优化主要在于I/O流的重复启动。例如如果输入的指令有重复的,则I/O流需要重复操作,不断重复代码的运行,代码运行的重复率较高
- 输入流用FileWriter比较慢
5.2 改进
- 使用hashMap存储key与data充当缓存机制的实现体,key对应的是players这样的命令,而data对应的是需要读取进输出文件的内容。这样每次读取指令先对hashMap中的指令进行查找,如果查找到了,则将hashMap的内容输出到输出文化,减少了代码运行的计算时间
HashMap<String,String> cache = new HashMap<>();//用于模拟缓存,反正指令重复导致的反复读写IO
//每次读取前进行判断
if (cache.containsKey("players")) {
// 重复请求
writeInfo(cache.get("players"),outputFile);
return;
}
cache.put("players",playersInfo.toString());//第一次读取的时候
//每次读取前进行判断
if(cache.containsKey(date)){
writeInfo(cache.get(date),outputFile);
return;
}
cache.put(date,resultsInfo.toString());
- 使用BufferdWriter包装FileWriter去对输出流进行控制
//优化前
FileWriter fileWriter = new FileWriter(outputFile,true);
fileWriter.write(msg);
fileWriter.close();
//优化后
BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile,true));
bw.write(msg);
bw.close();
对如图
这样测试之后改进前的程序运行时间为:
这样测试之后改进后的程序运行时间为:
速度快了将近500ms,速度快了将近5/7
6. 单元测试
测试采用java的Juit进行测试,在对应函数上写入@Test注释并按alt+enter进行maven依赖的导入
- 对AOSearch.java的单元测试(主要对参数不同,与参数错误进行测试)
@Test
public void test1(){
//错误的参数输入
AOSearch.main(new String[]{"input.txt"});
}
@Test
public void test1_1(){
//错误的参数输入
AOSearch.main(new String[]{"input"});
}
@Test
public void test2(){
//正确的参数个数输入
AOSearch.main(new String[]{"input.txt","output.txt"});
}
@Test
public void test2_1(){
//正确的参数个数输入
AOSearch.main(new String[]{"input.txt","put.txt"});
}
@Test
public void test2_2(){
//错误的参数输入,结果可以输出put文件,可正常写入,且可以用记事本查看
AOSearch.main(new String[]{"input.txt","put"});
}
@Test
public void test2_3(){
//错误的参数输入结果可以输出put.p文件,可正常写入,且可以用记事本查看
AOSearch.main(new String[]{"input.txt","put.p"});
}
@Test
public void test3(){
//不正确的参数输入,覆盖率较高,程序运行较为成功
AOSearch.main(new String[]{"input.txt","output.txt","others.txt"});
}
@Test
public void test3_1(){
//不正确的参数输入,覆盖率较高
AOSearch.main(new String[]{"input.txt","output","others.txt"});
}
测试结果
1、对test1的测试
结果分析:class类的覆盖率50是因为,包中的测试类未实现,而方法覆盖率低是因为穿啊如的参数错误,代码执行的少,程序结束的快
2、对test1_1的测试
3、对test2的测试
4、对test2_1的测试
5、对test2_2的测试
6、对test2_3的测试
结果符合预期
- 对Lib接口的测试(主要对,各种不一样参数的输入进行判断)
@Test
public void test1() {
try{
Lib.writePlayers("output.txt");
Lib.writeInfo("aa","output.txt");
Lib.writeResults("0116","output.txt");
Lib.writeResults("0130","output.txt");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void test1_1() {
try{
Lib.writePlayers("output");
Lib.writePlayers("output.pp");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void test1_2() {
try{
Lib.writeResults("Q55","output");
Lib.writeResults("Q55","out.txt");
Lib.writeResults("","out.txt");
}catch (Exception e){
e.printStackTrace();
}
}
@Test
public void test1_3() {
try{
Lib.writeResults("-11","out.txt");
Lib.writeResults("-11","ahs");
}catch (Exception e){
e.printStackTrace();
}
}
-
对test1的测试结果
-
对test1_1的测试结果
-
对test1_2的测试结果
-
对test1_3的测试结果
总结:类覆盖率达到100%,是因为所以包中的类都对其进行了实现,方法覆盖率无法进一步提升是因为io流的限制
- 实现自动单元测试
1、
2、
3、
4、挑选需要进行测试的member文章来源:https://www.toymoban.com/news/detail-840284.html
7. 异常处理
- 总体使用try…catch…finally对代码进行维护
public class AOSearch implements Lib{
public static void main(String[] args) {
//毫秒ms:
long startTime=System.currentTimeMillis(); //获取开始时间
...
try{...}
}catch(Exception e){
e.printStackTrace();
System.out.println("错误");
}finally {
long endTime=System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
}
}
}
- 还有抛出异常,返回给调用者处理
- 例如:
static String getJSONStr(String fileName) throws IOException{
File file = new File(fileName);
if (file.exists()){
//检查json文件是否存在
FileInputStream fileInputStream = new FileInputStream(file);
int length = fileInputStream.available();
byte[] bytes = new byte[length];
fileInputStream.read(bytes);
String JSONStr = new String(bytes);//从文件中获取json字符串
fileInputStream.close();
return JSONStr;
}else{
throw new FileNotFoundException("未找到" + fileName);
}
}
8. 心得体会
这次作业主要的难点在于对于json数据的解析、单元测试的编写、性能的提升、还有打包文件的过程。打包文件为jar包每次都会碰到奇怪的问题,要花大量时间去处理。分析题目不难,难在对于输入各种异常结果的输出测试,文件结构相对简单,但是编写过程会遇到杂七杂八的问题需要查找与解决。文章来源地址https://www.toymoban.com/news/detail-840284.html
到了这里,关于软件工程实践第二次作业---文件读取的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!