这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/2020SPRINGS |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/2020SPRINGS/homework/10287 |
这个作业的目标 | github初使用 代码规范制定 疫情统计程序 |
作业正文 | https://www.cnblogs.com/Xily9/p/12284708.html |
其他参考文献 | 无 |
1. Github仓库地址
https://github.com/Xily9/InfectStatistic-main
2. PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
Estimate | 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 400 | 385 |
Analysis | 需求分析 (包括学习新技术) | 30 | 15 |
Design Spec | 生成设计文档 | 10 | 10 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
Design | 具体设计 | 15 | 15 |
Coding | 具体编码 | 240 | 180 |
Code Review | 代码复审 | 20 | 20 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 120 |
Reporting | 报告 | 90 | 150 |
Test Repor | 测试报告 | 20 | 20 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 120 |
合计 | 500 | 545 |
3. 解题思路描述
-
命令行参数处理
Java中参数是以字符串数组形式传入,根据题目要求list命令 支持以下命令行参数:
-log 指定日志目录的位置,该项必会附带,请直接使用传入的路径,而不是自己设置路径
-out 指定输出文件路径和文件名,该项必会附带,请直接使用传入的路径,而不是自己设置路径
-date 指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期之前的所有log文件
-type 可选择[ip: infection patients 感染患者,sp: suspected patients 疑似患者,cure:治愈 ,dead:死亡患者],使用缩写选择,如 -type ip 表示只列出感染患者的情况,-type sp cure则会按顺序【sp, cure】列出疑似患者和治愈患者的情况,不指定该项默认会列出所有情况。
-province 指定列出的省,如-province 福建,则只列出福建,-province 全国 浙江则只会列出全国、浙江可以得知第一个参数必是list,所以可忽略list参数,直接以-开头的参数开始处理,根据上面的要求可得知一个参数的值可以为多个,这里可以在读取到以-的参数时记录当前处理的参数,以便处理后续值时得知正在处理的参数
然后使用一个命令类保存读取到的命令以便后续处理 -
日志文件的读取
根据题目要求,日志文件以日期名命名,日志的形式有以下8种<省> 新增 感染患者 n人
<省> 新增 疑似患者 n人
<省1> 感染患者 流入 <省2> n人
<省1> 疑似患者 流入 <省2> n人
<省> 死亡 n人
<省> 治愈 n人
<省> 疑似患者 确诊感染 n人
<省> 排除 疑似患者 n人首先是获取文件列表,要手动按文件名排序一次,这样可确保先读取的文件日期更早
然后按行读取日志文件的内容,这里考虑到了效率问题,所以未考虑使用正则表达式匹配,采用直接按空格切割文本并提取关键字的形式来匹配日志形式(虽然这样做并不优雅),然后读取到的内容用一个Log列表保存,其中保存了读取的每一行日志的信息(有流入流出的省份分成两个不同省份的Log处理,其中一条日志的数量为正,另一条为负(下同),死亡 治愈 确诊用同一省份的两条Log表示) -
日志处理
遍历读取到的日志列表,保存进一个类似嵌套Map的结构当中,之后以是否要输出全国数据判断是否要计算全国数据 -
结果输出
根据题目要求全国总是排第一个,别的省按拼音先后排序
于是上网查找排序方法,然后将Map的内容输出到给定的输出文件当中
4. 设计实现过程
-
命令行参数处理
readArgs
方法流程图如下
用于存储命令的
Command
类类图如下
-
日志文件的读取
readLog
方法流程图如下
用于存储每行日志信息的
Log
类类图如下
-
日志处理
日志处理相关流程图如下
-
结果输出
结果输出相关流程图如下
5. 代码说明
- 命令行参数处理
public static Command readArgs(String[] args) { //判断参数长度以及第一个参数是否为list if (args.length == 0 || !args[0].equals("list")) { return null; } String argType = "";//临时保存正在处理的参数 Command.Builder builder = new Command.Builder(); //准备构造Command //遍历参数,略过第一个参数 for (int i = 1; i < args.length; i++) { String arg = args[i]; //参数是否以-开头 if (arg.startsWith("-")) { argType = arg; } else { //判断正在处理的参数类型 switch (argType) { case "-log": builder.setLogDir(arg); break; case "-out": builder.setOutFile(arg); break; case "-date": builder.setDate(arg); break; case "-type": builder.addPrintType(arg); break; case "-province": builder.addPrintProvince(arg); break; default: //参数不符合要求直接返回空 return null; } } } return builder.build();//构造完毕,返回构造好的Command类 }
- 判断日期合理性
List<String> files = Lib.getFiles(command.getLogDir()); if (command.getDate() != null) {//只有传入的日期参数不为空才要判断合理性 //这里用了一种投机取巧的方法,直接用传入的日期构造文件名与输入目录获取到的最后一个文件名做文本比对 if (files.get(files.size() - 1).compareTo(command.getDate() + ".log.txt") < 0) { System.out.println("日期超出范围"); return; } }
- 读取日志内容
public static List<Log> readLog(File file) { List<Log> logList = new ArrayList<>(); try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { String s; while ((s = bufferedReader.readLine()) != null) { //以//开头的行直接跳过 if (s.startsWith("//")) continue; String[] arr = s.split(" ");//分割单行内容 if (arr.length == 5) {//长度是5,表示是有流入的情况 Log logOut = new Log(); int count = Integer.parseInt(arr[4].substring(0, arr[4].length() - 1));//再次截取人数,去掉“人” logOut.setProvince(arr[0]); logOut.setType(arr[1]); logOut.setCount(-count);//流出省份人数为负 logList.add(logOut); Log log = new Log(); log.setProvince(arr[3]); log.setType(arr[1]); log.setCount(count);//流入省份人数为正 logList.add(log); } else if (arr.length == 4) {//长度为4,表示是新增 确诊 排除这些情况 int count = Integer.parseInt(arr[3].substring(0, arr[3].length() - 1)); Log log = new Log(); //因为情况有多种,所以提取关键字再次比对 switch (arr[1]) { case "新增": log.setProvince(arr[0]); log.setType(arr[2]); log.setCount(count);//新增人数自然是增多 break; case "疑似患者": Log logSub = new Log(); logSub.setProvince(arr[0]); logSub.setType("疑似患者"); logSub.setCount(-count);//疑似确诊,所以疑似人数减少 logList.add(logSub); log.setProvince(arr[0]); log.setType("感染患者"); log.setCount(count);//感染人数增多 break; case "排除": { log.setProvince(arr[0]); log.setType("疑似患者"); log.setCount(-count);//排除之后疑似患者就会减少 } break; } logList.add(log); } else {//长度为3,表示死亡 治愈这两种情况 int count = Integer.parseInt(arr[2].substring(0, arr[2].length() - 1)); Log logSub = new Log(); logSub.setProvince(arr[0]); logSub.setType("感染患者"); logSub.setCount(-count);//不管是死亡还是治愈,感染人数都是减少的 logList.add(logSub); Log log = new Log(); log.setProvince(arr[0]); log.setType(arr[1]); log.setCount(count);//死亡/治愈人数增加 logList.add(log); } } } catch (Exception e) { e.printStackTrace(); } return logList; }
- 日志输出
public static boolean writeResult(Command command, Map<String, Statistics> statisticsMap, String arg) { try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(command.getOutFile()))) { //遍历预定义的省份 for (String province : provinces) { //判断当前遍历到的省份是否在要输出的省份当中 if (command.getPrintProvinces().contains(province)) { bufferedWriter.write(province); Map<String, Integer> infos = statisticsMap.get(province).getInfos(); command.getPrintTypes().forEach(s1 -> { try { bufferedWriter.write(" " + s1 + infos.get(s1) + "人"); } catch (Exception e) { e.printStackTrace(); } }); bufferedWriter.newLine(); } } bufferedWriter.write("// 该文档并非真实数据,仅供测试使用"); bufferedWriter.newLine(); bufferedWriter.write("// 命令 " + arg); return true; } catch (Exception e) { e.printStackTrace(); } return false; }
6. 单元测试截图和描述
- 测试读取参数
- 测试获取目录文件
- 测试读取日志
- 测试main方法
7. 单元测试覆盖率优化和性能测试,性能优化截图和描述
-
单元测试覆盖率
由下截图可见,除了部分Getter/Setter这些不影响运行结果的代码外,单元测试基本覆盖了所有代码
-
性能测试
-
性能优化
将省份排序方式由Collator
提供的排序方式改为直接遍历排序好的省份列表判断是否需要输出,性能有显著提升
8. 代码规范的链接
https://github.com/Xily9/InfectStatistic-main/blob/master/221701423/codestyle.md
9. 心路历程与收获
- 感受
这次的代码部分还是比较容易的 麻烦的是之后的测试以及博客书写= = - 收获 :
- 进一步熟悉了Github 制定了代码规范
- 了解了Java中拼音排序的方法
- 熟悉了Java进行单元测试的方法和性能测试方法
- 进一步熟悉了Java语法
10. 技术路线图相关的5个仓库
- sunflower
https://github.com/android/sunflower
Google官方Jetpack Demo - OpenHub
https://github.com/ThirtyDegreesRay/OpenHub
一款优秀的Github Android客户端 - awesome-android-cn
https://github.com/jobbole/awesome-android-cn
Android资源大全 - AndroidCustomView
https://github.com/lygttpod/AndroidCustomView
自定义View的几个例子 - RxPermissions
https://github.com/tbruyelle/RxPermissions
基于RxJava2的Android运行时权限管理