这个作业属于哪个课程 | 2020春|S班(福州大学) |
---|---|
这个作业要求在哪里 | 软工实践寒假作业(2/2) |
这个作业的目标 | 学习github的使用,设计并且开发一个疫情统计程序,制定属自己的代码规范,学习设计测试用例并编写单元测试 |
作业正文 | 寒假作业(2/2) |
其他参考文献 | 博客,百度百科,知乎,CSDN,《码出高效_阿里巴巴Java开发手册》等相关内容 |
一.作业仓库
- 作业的主仓库:https://github.com/kofyou/InfectStatistic-main
- 个人仓库:https://github.com/fate-day/InfectStatistic-main
二.《构建之法》1~3章学习
1.《构建之法》学习心得
第一章 概论
这一章主要讲的是一些比较概念型的理论,我在这一章也初步了解软件工程与计算机科学的关系。并且知道了软件工程是一个不断研究、测试、更新的过程,一个软件总是存在一些bug,需要不断的维护和更新。
第二章 个人技术和流程
对于软件测试,从一开始就有听到过,但却很少去这么做,一直以为软件测试,就不断的启动程序然后用不同的方法测试。但是看完本章之后,才知道我当初的看法有问题。因为平时在写代码的时候,根本就没有做过这样的事情,读了这一章之后才知道,其实测试在软件开发的过程是一个很重要的过程,对于测试也有了更深入的了解,但具体要用到自己写的程序中,还需要一段时间的学习。
而对于个人开发流程,平时都没有怎么做,主要的时间都花在代码的编写上。
第三章 软件工程师的成长
这一章讲的是如何评价软件工程师水平的主要方法,也提到了初级软件工程师如何成长的问题,对于自己目前处于的阶段,也有了一定的把握,就是有很多东西自己还不知道,还要更加努力的学习。
2.PSP
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 25 |
Estimate | 估计这个任务需要多少时间 | 905 | 1005 |
Development | 开发 | 845 | 920 |
Analysis | 需求分析 (包括学习新技术) | 90 | 80 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | 30 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 45 | 50 |
Design | 具体设计 | 30 | 40 |
Coding | 具体编码 | 540 | 600 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 70 |
Reporting | 报告 | 60 | 85 |
Test Repor | 测试报告 | 30 | 45 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 30 |
合计 | 925 | 1030 |
三.解题思路
1.拿到题目时,先看题目,明确题目的要求。
要求:
(1).需要程序能够列出全国和各省在某日的感染情况。
(2).可以读取对应路径下的所有日志,然后处理日志和命令,在对应的输出路径下输出output.txt文档,列出全国和所有省的情况(全国总是排第一个,别的省按拼音先后排序)。
(3).list命令 支持以下命令行参数:
-log
指定日志目录的位置,该项必会附带,请直接使用传入的路径,而不是自己设置路径
-out
指定输出文件路径和文件名,该项必会附带,请直接使用传入的路径,而不是自己设置路径
-date
指定日期,不设置则默认为所提供日志最新的一天。你需要确保你处理了指定日期以及之前的所有log文件
-type
可选择[ip: infection patients 感染患者,sp: suspected patients 疑似患者,cure:治愈 ,dead:死亡患者],使用缩写选择,如-type ip
表示只列出感染患者的情况,-type sp cure
则会按顺序【sp,cure】列出疑似患者和治愈患者的情况,不指定该项默认会列出所有情况。
-province
指定列出的省,如-province 福建
,则只列出福建,-province 全国 浙江
则只会列出全国、浙江
2.看完要求之后,按照要求来对应解题。
思路:
(1).因为要列出全国和各省在某日的感染情况,其中包含很多的数据要记录,我想到的是用几个String数组来记录数据。
(2).要从传入的数据中读取命令,并且还要根据命令参数的相应要求来解析这些命令。
(3).对照解析的命令来读取相应的数据进行处理,并且输出到指定的txt文档。
3.我的设计路线
(1).读取命令。
(2).解析命令并判断命令有无错漏。
(3).执行命令,按照命令进行相应的处理。
(4).打开命令中需求的日志文件。
(5).处理读取到的日志文件中的信息,并将处理后的信息保存。
(6).将保存的信息输出要求路径的TXT文档(如果文档不存在就新建一个)。
4.资料的查找
我知道开始的时候漫无目的的找资料只会浪费时间,所有我先看了附录后那些参考资料,接着我先在百度上查找了命令行参数的相关信息,让我了解了其作用。在写代码的过程中遇到不会的地方,可以直接的查找相应的资料。
四.代码设计实现过程
1.用几个String数组来记录数据。
/*
* 设置省份列表
* 0代表未列出,1代表列出
*/
public int [] area = {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
public String[] areaStr = new String[]{"全国","安徽","北京","重庆","福建","甘肃","广东",
"广西","贵州","海南","河北","河南","黑龙江","湖北","湖南","吉林","江苏","江西",
"辽宁","内蒙古","宁夏","青海","山东","山西","陕西","上海","四川","天津","西藏",
"新疆","云南","浙江"};
/*
* 设置患者类型(ip,sp,cure,dead)
* 0代表无需列出,1代表感染,2代表疑似,3代表治愈,4代表死亡
* 默认排序为1,2,3,4
*/
public int [] patients = {1,2,3,4};
public String[] patientsStr = new String[] {"感染患者","疑似患者","治愈","死亡"};
/*
* 设置全国和各地各个患者类型的人数情况
* 数组的一维代表地区(包括全国),二维代表患者类型(ip,sp,cure,dead)
*/
public int [][] totalNumber = new int [32][4];
2.设计一个类来专门处理命令
创建一个Analysis类,包含了JudgeParameter,GetArea,GetOriginPath,GetTargetPath,GetDate,GetType,IsDate等函数来解决相关问题。
3.设计一个类来专门处理关于文件的问题
创建一个FileProcess类,包含了ReadLogFile,SortLogFile,LogFile,LogProcess,AddPeople,DeadPeople,CurePeople,ExcludePeople,InflowPeople,ConfirmPeople,WriteOutFile等函数来解决相关问题。
五.代码说明
1.判断命令行参数是否有误
(1).先会判断list
命令有无输入,如果没有就报错。
(2).读取其后的参数进行判断,用if/else if的方法来判断各个命令行参数出现的情况,如果出现就转入其他的判断函数进行读取判断。如果没有参数也会报错。
public boolean JudgeParameter(){
if (args[0] != "list") {
System.out.println("未输入命令");
return false;
}
int i = 0;
for (i = 1 ; i < args.length ; i++) {
if (args[i] == "-log") {
if (GetOriginPath(++i) == false) {
System.out.println("日志目录路径格式错误");
return false;
}
break;
}
}
for (i = 1 ; i < args.length ; i++) {
if (args[i] == "-log")
i++;
else if (args[i] == "-out") {
if (GetTargetPath(++i) == false) {
System.out.println("输出文件路径格式错误");
return false;
}
}
else if (args[i] == "-date") {
if (GetDate(++i) == false) {
System.out.println("日期超出范围");
return false;
}
}
else if (args[i] == "-type") {
var = 0;
int n = 0;
for (n = 0 ; n < 4 ; n++) {
patients[n] = 0;
}
if (GetType(++i) == false) {
System.out.println("患者类型参数错误");
return false;
}
System.out.println(var);
System.out.println(i);
if (var > 0) {
i = i + var - 1;
}
}
else if (args[i] == "-province") {
var = 0;
preMark = 1;
area[0] = 0;
if (GetArea(++i) == false) {
System.out.println("省份参数错误");
return false;
}
if (var > 0) {
i = i + var - 1;
}
}
else {
System.out.println("无命令参数输入");
return false;
}
}
return true;
}
2.获取输出文件路径并判断输出文件路径是否格式正确
(1).用matches()方法来判断路径的格式是否正确,正确就赋值给targetPath。获取日志目录路径的方法和其一样就不列出。
public boolean GetTargetPath (int i) {
if (i < args.length) {
if (args[i].matches("^[A-z]:\\(\S+)+(\.txt)$")) {
targetPath = args[i];
return true;
}
else {
return false;
}
}
else{
return false;
}
}
3.读取日志文件下的相关文档
public void ReadLogFile () {
File file = new File(originPath);
File[] tempList = file.listFiles(); //将日志文件下的所有的文档路径写入File数组
SortLogFile(tempList);
}
4.读取日志文件信息
(1).将筛选出来符合要求的日志文件文档的信息读取出来。
(2).用!line.startsWith("//")
使得以“//”开头的行不会被读出来。
public void LogFile (String fileName) {
String filePath = originPath + fileName;
try {
File tempFile = new File(filePath);
InputStreamReader reader = new InputStreamReader(
new FileInputStream(tempFile),"UTF-8"); // 建立一个输入流对象reader
BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言
String line = "";
while ((line = br.readLine()) != null) {
if(!line.startsWith("//"))
LogProcess(line);
}
br.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
5.将数据写入txt文件
(1).判断要写入的患者类型有几种,数量记录为t
。
(2).进行一个双重循环的判断来将内容写入输出的txt文档。
public void WriteOutFile () {
try {
File writeName = new File(targetPath); // 相对路径,如果没有则要建立一个新的output.txt文件
writeName.createNewFile(); // 创建新文件
BufferedWriter out = new BufferedWriter(new FileWriter(writeName));
int n = 0 , m = 0 , k = 0 , t = 0;
String outData="";
for (n = 0 ; n < 4 ; n++) {
if (patients[n]!=0)
t++;
}
for (n = 0 ; n < 32 ; n++) {
if (area[n] == 1) {
k = 0;
outData = areaStr[n]+" ";
for (m = 0 ; m < 4 ; m++) {
if (patients[m] == 1) {
outData += patientsStr[0] + totalNumber[n][m] + "人" ;
k++;
}
else if (patients[m] == 2) {
outData += patientsStr[1] + totalNumber[n][m] + "人" ;
k++;
}
else if (patients[m] == 3) {
outData += patientsStr[2] + totalNumber[n][m] + "人" ;
k++;
}
else if (patients[m] == 4) {
outData += patientsStr[3] + totalNumber[n][m] + "人" ;
k++;
}
if (k < t) {
outData += " ";
}
}
out.write(outData + "
");
}
}
out.write("// 该文档并非真实数据,仅供测试使用
");
out.flush(); // 把缓存区内容压入文件
out.close(); // 最后记得关闭文件
}
catch (Exception e) {
e.printStackTrace();
}
}
}
六.单元测试截图和描述
单元测试总的12个测试结果:
所有的测试所用数据都来自于example中的log文件里的文档
(1).第一次测试没有运用-type -province
命令行参数,-date
指定的日期为最早日志的前一天。因为这一天没有日志记录,所有只输出了全国的信息,且信息都为0。
测试代码:
@Test
public void test() {
String[] str = {
"list","-date","2020-01-21","-log","F:\221701124\log\","-out",
"F:\221701124\output.txt"
};
InfectStatistic.main(str);
}
(2).第二次测试没有运用-type -province
命令行参数,-date
指定的日期为最早日志的那一天。因为日志中只有福州,湖北的信息,所有按默认的输出全国,福州,湖北。
测试代码:
@Test
public void test1() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output1.txt","-date","2020-01-22"
};
InfectStatistic.main(str);
}
(3).第三次测试没有运用-type -province
命令行参数,-date
指定的日期为最晚日志的那一天。因为日志中只有福州,湖北的信息,所有按默认的输出全国,福州,湖北。
测试代码:
@Test
public void test2() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output2.txt","-date","2020-01-27"
};
InfectStatistic.main(str);
}
(4).第四次测试没有运用-type -date
命令行参数,-province
指定的地区为全国,福州,湖北。因为没有运用-date
参数,所有默认时间为当期的日期。
测试代码:
@Test
public void test3() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output3.txt","-province","全国","福建","湖北"
};
InfectStatistic.main(str);
}
(5).第五次测试没有运用-type -date
命令行参数,-province
指定的地区为全国,浙江显示的地区就为这些指定的地区又因为浙江在日志中没有记录,所有显示出的数据全为0。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test4() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output4.txt","-province","全国","浙江"
};
InfectStatistic.main(str);
}
(6).第六次测试没有运用-type -date
命令行参数,-province
指定的地区为全国,福州显示的地区就为这些指定的地区。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test5() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output5.txt","-province","全国","福建"
};
InfectStatistic.main(str);
}
(7).第七次测试没有运用-type -date
命令行参数,-province
指定的地区为湖北,全国,福建显示的地区就为这些指定的地区,但是省份的排序还是按照默认的(全国在首位,其余省份按拼音排序)。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test6() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output6.txt","-province","湖北","全国","福建"
};
InfectStatistic.main(str);
}
(8).第八次测试没有运用-province -date
命令行参数,-type
指定的患者类型为ip,sp,cure,dead。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test7() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output7.txt","-type","ip","sp","cure","dead"
};
InfectStatistic.main(str);
}
(9).第九次测试没有运用-province -date
命令行参数,-type
指定的患者类型为sp,ip,dead,cure,输出的患者类型的数据就是按照指定的排序来排。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test8() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output8.txt","-type","sp","ip","dead","cure"
};
InfectStatistic.main(str);
}
(10).第十次测试没有运用-province -date
命令行参数,-type
指定的患者类型为sp,ip,输出的患者类型的数据就是按照指定的排序来排,且因为只有sp,ip,所以只列出sp,ip的数据。因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test9() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output9.txt","-type","sp","ip"
};
InfectStatistic.main(str);
}
(11).第十一次测试没有运用-date
命令行参数,-type
指定的患者类型为sp,ip,输出的患者类型的数据就是按照指定的排序来排,且因为只有sp,ip,所以只列出sp,ip的数据。-province
指定的地区为全国,浙江显示的地区就为这些指定的地区又因为浙江在日志中没有记录,所有显示出的数据全为0。又因为没有运用-date
,所有默认时间为当期的日期。
测试代码:
@Test
public void test10() {
String[] str = {
"list","-log","F:\221701124\log\","-out",
"F:\221701124\output10.txt","-type","sp","ip","-province","全国" ,"浙江"
};
InfectStatistic.main(str);
}
(12).第十二次测试-type
指定的患者类型为sp,ip,输出的患者类型的数据就是按照指定的排序来排,且因为只有sp,ip,所以只列出sp,ip的数据。-province
指定的地区为全国,浙江显示的地区就为这些指定的地区又因为浙江在日志中没有记录,所有显示出的数据全为0。-date
指定的时间为日志最早的一天,所以只输出关于这天的各省患者数据。
测试代码:
@Test
public void test11() {
String[] str = {
"list","-date","2020-01-22","-log","F:\221701124\log\","-out",
"F:\221701124\output11.txt","-type","sp","ip","-province","全国" ,"浙江"
};
InfectStatistic.main(str);
}
七.单元测试覆盖率优化和性能测试
单元测试覆盖率
这是代码的单元测试覆盖率,因为剩下没覆盖到的代码基本为输入命令行参数有误时报错的代码,所以覆盖率优化比较难。
性能优化
我的程序的性能图如下:
关于性能优化我有以下几点看法,当自己没有什么好的办法解决:
1.因为代码中大量的使用判断语句,导致了很多不必要的判断。
2.代码中有大量的多重循环语句,尤其是在判断省份,患者类型是否列出,和判断省份是否要写入输出文档,还有拆分日志文件路径时。
八.代码规范
九.心路历程与收获
1.心路历程
在本次的作业完成过程中,最开始我对于作业十分的茫然,不知道从什么地方开始下手。接着靠着助教发的提示(其他人问的问题的回答文档)和作业后面附录的帮忙,找到了前进的方向,不再是没有头绪。有了方向后写代码就清晰,知道自己要写什么,我就觉得自己豁然开朗。
2.收获
(1).在本次作业中我第一次知道了怎么进行单元测试和检测代码的覆盖率。之前我写代码都没进行单元测试,也没考虑检测代码的覆盖率。我感觉这是我本次最大的收获之一。
(2).我还在这次作业中学会了怎么使用github。我可以在github上找到很多优秀的代码和说明,github的使用让团队合作更方便。
(3).阅读了《构建之法》的1~3章后,第二章让我对于单元测试,回归测试,效能分析工具有了一定的印象。
(4).让我复习了Java的相关知识。
十.Java后端学习相关的5个仓库
1.javaok
链接:https://github.com/xjjdog/javaok
简介:关于学习Java的基础知识、Java基础知识、Java进阶知识的分类,还有最常用最重要的工具的介绍。
2.3y
链接:https://github.com/ZhongFuCheng3y/3y
简介:从Java基础、JavaWeb基础到常用的框架再到面试题都有完整的教程,几乎涵盖了Java后端必备的知识点。
3.advanced-java
链接:https://github.com/doocs/advanced-java
简介:互联网 Java 工程师进阶知识完全扫盲,内容涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识。
4.java_backend
链接:https://github.com/wonderfulstudy/java_backend
简介:关于Java后端学习的资料大全,包含Java后端自学路线图和学习路线图。
5.JavaGuide
链接:https://github.com/Snailclimb/JavaGuide
简介:【Java学习+面试指南】一份涵盖大部分Java程序员所需要掌握的核心知识。