寒假作业2/2
1.概述
项目 | 内容 |
---|---|
这个作业属于哪个课程 | <班级的链接> |
这个作业要求在哪里 | <作业要求的链接> |
这个作业的目标 | Git、GitHub使用,代码规范意识,一定的程序设计能力(基于命令行),PSP,以及单元测试和性能分析改进。 |
作业正文 | 见正文 |
其他参考文献 | 无 |
2.Git Hub仓库连接
https:// github.com/ maouou /InfectStatistic-main -> 点击进入
3.PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 25 |
Estimate | 估计这个任务需要多少时间 | 910 | 1125 |
Development | 开发 | 480 | 720 |
Analysis | 需求分析 (包括学习新技术) | 120 | 120 |
Design Spec | 生成设计文档 | 30 | 40 |
Design Review | 设计复审 | 30 | 15 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 15 |
Design | 具体设计 | 30 | 300 |
Coding | 具体编码 | 420 | 360 |
Code Review | 代码复审 | 30 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 50 |
Reporting | 报告 | 60 | 90 |
Test Repor | 测试报告 | 30 | 20 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 45 |
合计 | 970 | 1150 |
4.解题思路
-
实话实说,第二次作业看到以后我是蒙圈的
然后下定决心开始做的时候,我是按照作业要求上一步一步开始下手的。
之前没有接触过github,所以在这方面花了蛮久的时间去熟悉学习他,刚开始很痛苦,无从下手。之后干脆就根据github提供的学习手册->https://guides.github.com/activities/hello-world/来学习,用了很大一部分时间,才解决了Git和Github的使用问题。 -
之后开始理需求,这个过程还是比较轻松加愉快的,整体文档要求比较明确,读完之后,最初的设计思路是这样的:
5.设计实现过程
在实际编码设计过程中,对很多类进行了调整,也新增了很多需要的实体类和工具类。
例:
- 添加统计各省人数的数据结构 -> PeopleType
- 添加Command接口、CommandGet类、InfectSatisticCommandSet类、CommandList类用于实现命令行控制以及方便之后功能扩展
工作流程:
6.代码说明
关键函数
-
CommandGet. GetCommand(String[] CommandSource)
用于提取命令行中的参数并存储在该类的数据结构中,返回值为void
public void GetCommand(String[] CommandSource) { int CommandLength = CommandSource.length; if(CommandSource[0].equals("list")) this.Command = 1; else System.out.println(CommandSource[0] + " 不能被解析为合法命令"); for(int i = 1 ; i < CommandLength ; i++) { if(CommandSource[i].equals("-log")) { this.Log = CommandSource[i+1]; i++; } else if(CommandSource[i].equals("-out")) { this.Out = CommandSource[i+1]; i++; } else if(CommandSource[i].equals("-date")) { this.IsDate = true; this.Date = CommandSource[i+1]; i++; } else if(CommandSource[i].equals("-province")) { this.IsProvince = true; int j = 0; while(i+1<CommandSource.length&&!Pattern.matches("-.*", CommandSource[i+1])) { this.Province[j] = CommandSource[i+1]; i++; j++; } } else if(CommandSource[i].equals("-type")) { this.IsType = true; int j = 0; while(i+1<CommandSource.length && !Pattern.matches("-.*", CommandSource[i+1])) { this.Type[j] = CommandSource[i+1]; i++; j++; } } else System.out.println("命令中包含不合法参数" + CommandSource[i]); }
-
DateCompareTool.getFileName(String Date,String LogLocation)
根据log参数和date参数提供的信息,筛选出需要处理的文件路径集合,返回值为List
public static List<String> getFileName(String Date,String LogLocation) { List<String> FileName = new ArrayList<String>(); String[] FileList=new File(LogLocation).list(); int MaxMonth = 0; int MaxDay = 0; //下述判断用于解决是否带 -date参数问题 if(Date.equals("")) { for(int i = 0; i < FileList.length; i++) FileName.add(LogLocation + "\\" + FileList[i]); return FileName; } else { boolean DateOutbound = false; String[] SplitDate = Date.split("-"); String MonthDate = SplitDate[1]; String DayDate = SplitDate[2]; int TargetMonth = Integer.valueOf(MonthDate); int TargetDay = Integer.valueOf(DayDate); for(int i = 0;i < FileList.length ;i++) { String[] SplitFileName = FileList[i].split("-"); String Month = SplitFileName[1]; String Day = SplitFileName[2].split(".log")[0]; int FileMonth = Integer.valueOf(Month); int FileDay = Integer.valueOf(Day); if(FileMonth > MaxMonth) MaxMonth = FileMonth; if(FileDay > MaxDay) MaxDay = FileDay; if(FileMonth < TargetMonth) FileName.add(LogLocation + "\\" + FileList[i]); if(FileMonth == TargetMonth && FileDay <= TargetDay) FileName.add(LogLocation + "\\" + FileList[i]); } if(TargetMonth > MaxMonth || (TargetMonth == MaxMonth && TargetDay > MaxDay)) DateOutbound = true; if(DateOutbound) { System.out.println("日期超过范围"); System.exit(1); } return FileName; } }
-
DataGet.getData(String line)
根据每一行的信息,匹配8种信息模式,切割字符串,获得原始数据存储在该类的数据结构中,返回值void
public void getData(String line) { String Type1 = "\\W+ 新增 感染患者 \\d+人"; String Type2 = "\\W+ 新增 疑似患者 \\d+人"; String Type3 = "\\W+ 感染患者 流入 \\W+ \\d+人"; String Type4 = "\\W+ 疑似患者 流入 \\W+ \\d+人"; String Type5 = "\\W+ 死亡 \\d+人"; String Type6 = "\\W+ 治愈 \\d+人"; String Type7 = "\\W+ 疑似患者 确诊感染 \\d+人"; String Type8 = "\\W+ 排除 疑似患者 \\d+人"; if(Pattern.matches(Type1, line)) this.Type = 1; if(Pattern.matches(Type2, line)) this.Type = 2; if(Pattern.matches(Type3, line)) this.Type = 3; if(Pattern.matches(Type4, line)) this.Type = 4; if(Pattern.matches(Type5, line)) this.Type = 5; if(Pattern.matches(Type6, line)) this.Type = 6; if(Pattern.matches(Type7, line)) this.Type = 7; if(Pattern.matches(Type8, line)) this.Type = 8; String[] SplitLine = line.split(" "); if(this.Type == 1 || this.Type == 2) { this.Province1 = SplitLine[0]; this.Number = Integer.valueOf(SplitLine[3].split("人")[0]); } if(this.Type == 3 || this.Type == 4) { this.Province1 = SplitLine[0]; this.Province2 = SplitLine[3]; this.Number = Integer.valueOf(SplitLine[4].split("人")[0]); } if(this.Type == 5 || this.Type == 6) { this.Province1 = SplitLine[0]; this.Number = Integer.valueOf(SplitLine[2].split("人")[0]); } if(this.Type == 7 || this.Type == 8) { this.Province1 = SplitLine[0]; this.Number = Integer.valueOf(SplitLine[3].split("人")[0]); } }
-
StatisticResult.Statistic(DataGet data)
根据传入的原始数据进行数据统计,获得每个省的人数信息,存储在该类的数据结构中,返回值void
public void Statistic(DataGet data) { if(data.Type == 1) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Comfirmed += data.Number; StatisticLink.get(0).Comfirmed += data.Number; ProvinceOccur[ProvinceIndex_1] = true; } if(data.Type == 2) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Suspected += data.Number; StatisticLink.get(0).Suspected += data.Number; ProvinceOccur[ProvinceIndex_1] = true; } if(data.Type == 3) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); int ProvinceIndex_2 = ProvinceIndex.indexOf(data.Province2); StatisticLink.get(ProvinceIndex_1).Comfirmed -= data.Number; StatisticLink.get(ProvinceIndex_2).Comfirmed += data.Number; ProvinceOccur[ProvinceIndex_1] = true; ProvinceOccur[ProvinceIndex_2] = true; } if(data.Type == 4) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); int ProvinceIndex_2 = ProvinceIndex.indexOf(data.Province2); StatisticLink.get(ProvinceIndex_1).Suspected -= data.Number; StatisticLink.get(ProvinceIndex_2).Suspected += data.Number; ProvinceOccur[ProvinceIndex_1] = true; ProvinceOccur[ProvinceIndex_2] = true; } if(data.Type == 5) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Dead += data.Number; StatisticLink.get(0).Dead += data.Number; StatisticLink.get(ProvinceIndex_1).Comfirmed -= data.Number; StatisticLink.get(0).Comfirmed -= data.Number; ProvinceOccur[ProvinceIndex_1] = true; } if(data.Type == 6) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Healed += data.Number; StatisticLink.get(0).Healed += data.Number; StatisticLink.get(ProvinceIndex_1).Comfirmed -= data.Number; StatisticLink.get(0).Comfirmed -= data.Number; ProvinceOccur[ProvinceIndex_1] = true; } if(data.Type == 7) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Comfirmed += data.Number; StatisticLink.get(0).Comfirmed += data.Number; StatisticLink.get(ProvinceIndex_1).Suspected -= data.Number; StatisticLink.get(0).Suspected -= data.Number; ProvinceOccur[ProvinceIndex_1] = true; } if(data.Type == 8) { int ProvinceIndex_1 = ProvinceIndex.indexOf(data.Province1); StatisticLink.get(ProvinceIndex_1).Suspected -= data.Number; StatisticLink.get(0).Suspected -= data.Number; ProvinceOccur[ProvinceIndex_1] = true; } }
-
FileHandleTool.HandleFile(String DeadLine,String LogLocation,String[] Province, String[] Type)
根据log的值和deadline的值调用getFileName获取待处理文件后,按行读入,并将每行信息调用getData和Statistic函数统计结果,再按照province和type参数的值生成输出结果。返回值List
public static List<String> HandleFile(String DeadLine,String LogLocation,String[] Province, String[] Type) { List<String> FileNames = DateCompareTool.getFileName(DeadLine,LogLocation); StatisticResult Result = new StatisticResult(); for(int i = 0 ; i < FileNames.size() ; i ++) { String FilePath = FileNames.get(i); try { FileInputStream Is = new FileInputStream(FilePath); InputStreamReader Isr = new InputStreamReader(Is,"utf-8"); BufferedReader Br = new BufferedReader(Isr); String line; try { while((line = Br.readLine()) != null) { if(line.equals("")) continue; if(Pattern.matches("//.*", line)) continue; else { DataGet Use = new DataGet(); Use.getData(line); Result.Statistic(Use); } } } catch(IOException e) { e.printStackTrace(); System.err.println("读取一行文件错误"); } } catch(FileNotFoundException e) { e.printStackTrace(); System.err.println("文件路径错误,找不到指定文件"); } catch (UnsupportedEncodingException e1) { // TODO 自动生成的 catch 块 e1.printStackTrace(); } } if(Province[0].equals("")&&Type[0].equals("")) { for(int i = 0 ; i < Result.ProvinceOccur.length; i++) { if(Result.ProvinceOccur[i]) { OutResult.add(Result.ProvinceIndex.get(i) + " 感染患者" + Result.StatisticLink.get(i).Comfirmed + "人" + " 疑似患者" + Result.StatisticLink.get(i).Suspected + "人" + " 治愈" + Result.StatisticLink.get(i).Healed + "人" + " 死亡" + Result.StatisticLink.get(i).Dead + "人"); } } OutResult.add("// 该文档并非真实数据,仅供测试使用"); } if(Province[0].equals("") && !Type[0].equals("")) { for(int i = 0 ; i < Result.ProvinceOccur.length; i++) { if(Result.ProvinceOccur[i]) { String Data = Result.ProvinceIndex.get(i); for(int j = 0 ; j < 4 ; j ++) { if(Type[j].equals("")) break; else if(Type[j].equals("ip")) Data += " 感染患者" + Result.StatisticLink.get(i).Comfirmed + "人"; else if(Type[j].equals("sp")) Data += " 疑似患者" + Result.StatisticLink.get(i).Suspected + "人"; else if(Type[j].equals("cure")) Data += " 治愈" + Result.StatisticLink.get(i).Healed + "人"; else if(Type[j].equals("dead")) Data += " 死亡" + Result.StatisticLink.get(i).Dead + "人"; } OutResult.add(Data); } } OutResult.add("// 该文档并非真实数据,仅供测试使用"); } if(!Province[0].equals("") && Type[0].equals("")) { for(int i = 0 ; i < 32; i++) Result.ProvinceOccur[i] = false; for(int i = 0 ; i < 32; i++) { if(Province[i].equals("")) break; Result.ProvinceOccur[Result.ProvinceIndex.indexOf(Province[i])] = true; } for(int i = 0 ; i < Result.ProvinceOccur.length; i++) { if(Result.ProvinceOccur[i]) { OutResult.add(Result.ProvinceIndex.get(i) + " 感染患者" + Result.StatisticLink.get(i).Comfirmed + "人" + " 疑似患者" + Result.StatisticLink.get(i).Suspected + "人" + " 治愈" + Result.StatisticLink.get(i).Healed + "人" + " 死亡" + Result.StatisticLink.get(i).Dead + "人"); } } OutResult.add("// 该文档并非真实数据,仅供测试使用"); } else { for(int i = 0 ; i < 32; i++) Result.ProvinceOccur[i] = false; for(int i = 0 ; i < 32; i++) { if(Province[i].equals("")) break; Result.ProvinceOccur[Result.ProvinceIndex.indexOf(Province[i])] = true; } for(int i = 0 ; i < Result.ProvinceOccur.length; i++) { if(Result.ProvinceOccur[i]) { String Data = Result.ProvinceIndex.get(i); for(int j = 0 ; j < 4 ; j ++) { if(Type[j].equals("")) break; else if(Type[j].equals("ip")) Data += " 感染患者" + Result.StatisticLink.get(i).Comfirmed + "人"; else if(Type[j].equals("sp")) Data += " 疑似患者" + Result.StatisticLink.get(i).Suspected + "人"; else if(Type[j].equals("cure")) Data += " 治愈" + Result.StatisticLink.get(i).Healed + "人"; else if(Type[j].equals("dead")) Data += " 死亡" + Result.StatisticLink.get(i).Dead + "人"; } OutResult.add(Data); } } OutResult.add("// 该文档并非真实数据,仅供测试使用"); } return OutResult; }
-
OutputControlTool.ProductInfectStatistic(String DeadLine, String LogLocation,String ResultLocation,String[] Province,String[] Type)
调用HandleFile来获取结果,并将结果逐条存储到out参数所指定的目标文件中。返回值void
public static void ProductInfectStatistic(String DeadLine, String LogLocation,String ResultLocation,String[] Province,String[] Type) { File TargetFile = new File(ResultLocation); if(TargetFile.exists()) { System.out.println("该日志文档已存在,请修改目标文件名"); return ; } else { try { TargetFile.createNewFile(); } catch (IOException e) { System.err.println("该目标文件无法新建,请重试"); e.printStackTrace(); } } List<String> Result = FileHandleTool.HandleFile(DeadLine,LogLocation,Province,Type); try { OutputStream out = new FileOutputStream(TargetFile); BufferedWriter rd = new BufferedWriter(new OutputStreamWriter(out,"utf-8")); for(int i = 0; i < Result.size() ; i ++) rd.write(Result.get(i) + "\n"); rd.close(); out.close(); } catch(IOException e){ System.err.println("文件写入错误"); e.printStackTrace(); } System.out.println(ResultLocation + "文件已生成"); }
7.单元测试截图和描述
-
缺少必要的out参数
java InfectStatistic list -log D:
-
缺少必要的log参数
java InfectStatistic list -out D:\
-
测试-date、-log、-out参数
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log\ -out D:\Java\InfectStatistic-main\example\result\Out1.txt -date 2020-01-22
-
测试-province、-log、-out参数
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out2.txt -date 2020-01-22 -province 福建 河北
-
测试-type、-province、-log、-out参数
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out3.txt -date 2020-01-23 -type cure dead ip -province 全国 浙江 福建
-
日期超出范围
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out5.txt -date 2020-02-23
-
测试只有-log、-out参数,统计全部数据
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out4.txt
-
测试相同-out参数即目标文件已存在
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out4.txt
-
测试一个合法但不存在日志的日期
java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out5.txt -date 2020-01-25
8.单元测试覆盖率优化和性能测试,性能优化截图和描述。
-
覆盖率测试
list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\out6.txt -date 2020-01-27
分析:
- FileHandleTool覆盖较少主要是因为产生输出的时候,受province和type参数影响,产生了四个判断分支,因此覆盖较少
- CommandGet覆盖率不高主要是因为缺少province和type参数,因此提取参数时很多判断没有覆盖到
- DateCompareTool主要是因为获取待处理文件时,非法处理较多,因此影响了覆盖率
-
性能测试
- 程序中,较多地用到了ArrayList,其底层用数组实现,故char[]使用频率很高。
- 程序中嵌套较多,性能会比较差一些
- 频繁使用到切割字符串存贮比较,会影响耗时
-
优化
暂时没有什么好的优化方案。
9.代码规范的链接
10.心路历程与收获
真的刚要开始准备做这次作业的时候,内心世界是崩溃的。开始在心里觉得这次作业涉及面很广,感觉知识点又很多,整体很杂乱。再加之自己之前也没有github的使用经验。所以一开始从在哪里着手都不知道。之后好在助教发了一篇关于此次作业的一些提示和引导。于是我就从github的学习着手,一步一步学习使用各项功能,之后看懂了示例文件的结构,并成功地fork进了我的仓库和本地。至于之后的编码工作其实算法上没有什么难度,只是功能比较繁琐。
其实当我真正沉下心来开始一步步分析学习这次作业,也只是用了4个小时左右,就从毫无思绪的状态,到了感觉思路很明确各项功能门清的境地了。感觉心里已经有底了,之后就开始沉下心来设计、编码、测试、改bug。
通过这次作业我感觉我的收获有一个是复习了Java设计,当初学Java的时候,个人还是很喜欢Java语言的编程风格的,只是后来疏于联系,很多语法都不是那么熟练,这次作业让我将忘掉的语法又捡了起来。
其次就是学会了github进行代码管理,这真的是个很棒也很方便的平台,刚开始没有使用过的时候感觉很多功能都很繁杂,但熟悉之后就觉得他很便利也很简明。
再次就是了解学会了Eclipse的覆盖率测试和利用JProfiler进行性能测试。
最后,最重要的还是树立了信心吧。虽然我在软件工程和计算机方面真的不是很有灵性,但是所有的事情只要静下心去做,还是可以摸到门路,最后基本完成任务的!!!
11.相关的仓库
-
yii2-ueditor
基于yii2.0的百度编辑器扩展
-
JD
基于Yii2.0框架模仿打造京东电商平台
-
mzcx
使用YII2.0基于web端开发的打车软件
-
yii2_rbac_admin
Yii2.0 Rbac 示例教程
-
WebComponent
前端常用组件