这个作业属于哪个课程 | 2020春|S班(福州大学) |
---|---|
这个作业要求在哪里 | 软工实践寒假作业(2/2) |
作业的目标 | 学习使用Git、学习和了解效能分析及个人软件开发流程(PSP) |
作业正文 | 作业正文 |
其他参考文献 | 关于单元测试和回归测试 |
git仓库链接 | fzuwsh/InfectStatistic-main |
代码规范链接 | 我的代码规范 |
《构建之法》
个人软件开发流程(PSP)
PSP2.1 | Personal Software Process Stages | 预估耗时(min) | 实际耗时(min) |
---|---|---|---|
Planning | 计划 | 30 | 20 |
Estimate | 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 300 | 400 |
Analysis | 需求分析 (包括学习新技术) | 90 | 150 |
Design Spec | 生成设计文档 | 30 | 25 |
Design Review | 设计复审 | 10 | 5 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
Design | 具体设计 | 60 | 40 |
Coding | 具体编码 | 300 | 510 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 180 | 150 |
Reporting | 报告 | 60 | 100 |
Test Repor | 测试报告 | 40 | 30 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 1190 | 1510 |
解题过程
学习经历
当我看到作业的时候第一反应是啥也不会,虽然好像有挺长时间的,但是因为看不懂题目要做什么觉得很慌,但是觉得老师既然这么布置肯定认为我们绝大多数人在这段时间内可以完成新技术的学习和作业的完成,所以我便开始完成作业的规划。首先我看了一下我总共有哪些技术不会,Git、Github、单元测试、性能分析、PSP表格、参数处理(一开始以为也需要什么特殊的技术)。呃(⊙o⊙)… ,当时看完了确实觉得很绝望,我首先决定先学习Git(因为我当时觉得单元测试和性能分析是代码写完以后的事情,读《构建之法》的话看起来也不是很着急),所以我上网找到了廖雪峰的Git教程学习Git,在cmd了解了Git的基本语法,大致明白了工作原理,然后当我再去使用GitHub Desktop的时候就发现GitHub Desktop挺容易用的。在学习完Git后我就去大致的阅读了一下《构建之法》前三章,这时候我了解了PSP等知识,尤其是什么是单元测试有了初步的认识,这时我意识到测试数据应该在设计的时候就一起准备好了,而不是等到代码全部都写完了才去测试,因此在粗略的阅读《构建之法》后我改变了最初的想法,决定先学习如何使用IDEA进行单元测试和性能优化等知识。但是事情并不总是那么的顺利,我学习单元测试的路途中遇到了各种各样的问题,时间耽误了很多,等我终于大致明白要如何单元测试的时候我发现自己的时间已经不多了(大致还剩4-5天),因此我这时我也没有完全按照书中说的步骤,而是直接进行了本次作业的程序编写,程序编写完成后完成了单元测试部分。
解题思路
在我初步学习完大部分新技术后我还是构思设计本次作业,我首先是将该业务大致分成三个过程,(1)处理用户传入的参数,并将后面用来筛选的数据存储到内存;(2)将日志文件的数据读出并对省份的疫情情况进行更新(3)将需要的数据输出到指定文件;
对于几个关键问题的解决思路:(1)使用数组保存所有省份的名字,初始化时以拼音排序好,后面输出信息只需遍历数组顺序输出省名对应的ProvinceInfo类中的数据便可实现按拼音顺序操作。若未来想要以其他顺序排序,只需新添排序函数,改变数组中的省份顺序即可,不需要重写原有代码,符合设计模式中的开闭原则,也便于后期扩展。(2)若-province则输出日志中有提到的省份:在ProvinceInfo类中设置doesRefered成员变量用于表示是否提及,若无-province参数,只需在输出是添加doesRefered的判断语句即可。(3)-date超出最大范围的判断问题:在InfectStatistic类中设有List fileList 成员变量用于保存日志文件目录下的所有文件名,并进行排序,输入的-date参数只需与fileList中的最后一个日期相比较即可(fileList升序排序)。(4)全国的数据如何统计:在InfectStatistic类中设有各种情况的全国总数,每次循环改变总数,最后根据-province参数的情况决定是否输出全国总数。
面向对象设计(类图)
在分解完共能后我觉得上述(1)功能相对比较独立,因此我专门设计了一个ProcessParameter类用于完成参数处理功能;因为(2)是对各省的疫情情况数据的更新,而(3)是对该数据的输出,因此我认为二者可以合并到一个类InfectStatic类中。因为每个省的信息类别是一样的,而且该信息不适合作为InfectStatic类的成员变量,因此我单独设计了一个Province类保存一个省份的疫情信息。
核心代码说明
参数处理函数
本函数的形参是字符数组,用于传入main函数的参数数组,在函数内部将其转换为list变量(方便操作),对于只有单个值的参数(如-date、-log等)只需要获得改参数在list中的位置index,将下标为index+1的成员即该参数对应的值,将其保存到对应的成员变量。对于有多个值的参数(如-province、-type等)则通过findLastIndex方法获取多个值中最后一个值的索引lastIndex,并将list中index+1到lastIndex的数据保存。
public void processParameters(String []args){
List list = Arrays.asList(args);
int dateIndex = list.indexOf("-date");//截止日期.
String dateString = "";
if (dateIndex > 0) {//如果有传入-date参数
dateString = args[dateIndex + 1];
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
date = null;
if (dateIndex >= 0) {
try {
date = simpleDateFormat.parse(dateString);
} catch (ParseException e) {
System.out.println("时间参数非法");
System.exit(-1);
}
}
int dirIndex = list.indexOf("-log");//日志文件目录
if (dirIndex < 0) {
System.out.println("错误:没有传入日志文件路径");
System.exit(-1);
}
String dirPath = args[dirIndex + 1];//日志文件夹路径
logDir = new File(dirPath);//日志文件夹
int outputIndex = list.indexOf("-out");//输出文件目录
if (outputIndex < 0) {
System.out.println("错误:没有传入输出文件路径");
return;
}
outputPath = args[outputIndex + 1];
int provinceListIndex = list.indexOf("-province");//传入参数中-province的下标索引
int provinceListLastIndex = findLastIndex(args, provinceListIndex);//查询的最后一个省份的下标索引
provinceList = new ArrayList();//省份列表
if (provinceListIndex >= 0) {//有传入-province参数
for (int i = provinceListIndex + 1; i <= provinceListLastIndex; i++) {
provinceList.add(args[i]);
}
}
typeMap = new HashMap<>();//查询的类别ip、sp、cure、dead
int typeIndex = list.indexOf("-type");
int typeLastIndex = findLastIndex(args, typeIndex);
int t = typeIndex > 0 ? 0 : 1;//是否有传入-type参数 t=0:有 t=1:没有
typeMap.put("ip", t);
typeMap.put("sp", t);
typeMap.put("cure", t);
typeMap.put("dead", t);
if (typeIndex >= 0) {//有传入-type参数
for (int i = typeIndex + 1; i <= typeLastIndex; i++) {
typeMap.put(args[i], 1);
}
}
}
统计函数
统计函数传入参数dir为日志文件夹,date为查询的截止日期,首先将dir目录下所有的文件名(.log前的日期)依次加入fileList列表,在对列表进行升序排序,获取最早日期和最晚日期校验传入的截止日期是否合法。然后通过二重循环,依次读取每个文件的每一行(非注释行),通过调用函数updateProvinceInfo函数对数据进行更新保存。
public int statistic(File dir, Date date){//统计
try {
BufferedReader reader = null;
String line;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
for (File f : dir.listFiles()) {//将目录下所有的文件名加入fileList列表用于排序
fileList.add(f.getName().split("\\.")[0]);
}
Collections.sort(fileList);
String lastDateString = fileList.get(fileList.size() - 1);//最大日期
Date lastDate = simpleDateFormat.parse(lastDateString);
if (date != null && date.compareTo(lastDate) > 0) {
System.out.println("抱歉,日期超出范围");
return -1;
}
for (String fileDateString : fileList) {
File file = new File(dir.getPath() + "/" + fileDateString + ".log.txt");
Date fileDate = simpleDateFormat.parse(fileDateString);//日志日期
if (date != null && fileDate.compareTo(date) > 0) {
break;
}
reader = new BufferedReader(new FileReader(file));
while ((line = reader.readLine()) != null) {
if (line.charAt(0) != '/') {//该行不为注释
updateProvinceInfo(line);
}
}
}
}catch (Exception e){
}
return 0;
}
更新处理函数
该函数用于在读取数据后更新各省份的感染数据,思路是首先将该行的字符串以空格符(‘ ’)分割成数组,通过数组长度判断该行信息是对应哪种情况(3:死亡/治愈 4:新增/确诊/排除 5:从A省流入B省),再细分语句后通过省名利用provinceMap对对应的省份更新感染数据。
public void updateProvinceInfo(String line){
String message[] = line.split(" ");
ProvanceInfo province = provinceMap.get(message[0]);
String lastMessage = message[message.length-1];
int num = Integer.parseInt(lastMessage.substring(0,lastMessage.length()-1));//更新人数
switch (message.length){
case 3://死亡、治愈
province.setDoesRefered(true);
if(message[1].equals("死亡")){
province.diedNumAdd(num);
province.infectNumSub(num);
diedTotalNum += num;
infectTotalNum -= num;
}else{//治愈
province.cureNumAdd(num);
province.infectNumSub(num);
cureTotalNum += num;
infectTotalNum -= num;
}
break;
case 4://新增、确诊、排除
province.setDoesRefered(true);
if(message[1].equals("新增")){
if(message[2].equals("感染患者")){
province.infectNumAdd(num);
infectTotalNum += num;
}else{//疑似患者
province.suspectedAdd(num);
suspectedTotalNum += num;
}
}else if(message[1].equals("排除")){//排除疑似患者
province.suspectedSub(num);
suspectedTotalNum -= num;
}else{//确诊感染
province.suspectedSub(num);
province.infectNumAdd(num);
suspectedTotalNum -= num;
infectTotalNum += num;
}
break;
case 5://从A省流入B省
ProvanceInfo provinceB = provinceMap.get(message[3]);
province.setDoesRefered(true);
provinceB.setDoesRefered(true);
if(message[1].equals("感染患者")){
province.infectNumSub(num);
provinceB.infectNumAdd(num);
}else{//疑似患者
province.suspectedSub(num);
provinceB.suspectedAdd(num);
}
break;
}
}
单元测试
参数处理检验(1):只包含-date、-log、-out
该测试是调用processParameter对象的processParameters方法来处理传入的参数,在本次测试传入的参数只包含上述的三种参数,因此在processParameter对象的成员变量中date(截止日期)、outputPath(输出文件路径)、logDir(日志文件夹)的文件路径应与传入的参数相同,provinceList(查询省份列表)的个数为空,typeMap中的所有value(是否应该输出)都应为1(要输出)。
参数处理检验(2):包含-date、-log、-out、-province、-type参数(全部参数)
该测试传入的参数数组包含了所有种类的参数(-province 全国 福建 -type ip),所以在processParameter对象的成员变量中ate(截止日期)、outputPath(输出文件路径)、logDir(日志文件夹)的文件路径应与传入的参数相同,provinceList(查询省份列表)的长度为2,且包含"全国"和”福建“。typeMap中”ip“键对应的值为1(输出),其他均为0(不输出)。
参数处理检验(3):只包含-log、-out
该情况与检验(1)处理情况相似,唯一的区别是没有传入-date参数,因此在该测试下data的值应为null,且文件的输出应将所有日志文件统计后输出,省份因为没有指定应为所有日志中提到的省份
获取多值参数最后一个值的下标索引函数的检验
本测试主要是针对该参数是否是最后一种参数两种情况进行测试,如下图所示。其中第一个测试应返回数最后一个元素的下标(4),第二个测试应返回北京的下标(5)
日志文件夹处理测试
本测试主要针对InfectStatistic类的日志文件夹处理功能processLogDir方法,声明一个InfectStatistic对象,统计指定文件夹的日志文件,并检验日志文件个数,最大日期,最小日期
期限判定处理测试
本测试主要针对InfectStatistic类的日期范围判定功能processDate方法,程序是否能检验,返回值为0为没超出范围,-1为超出范围,当超出范围是会输出提示信息
更新省份传染信息函数测试
该测试主要是要检验当从文件读入不同的描述事件时(如新增、死亡等),函数是否能准确地更新相关省份的数据信息
性能测试
总览
内存情况
cpu使用情况
Live memory-classes
心路历程和收获
以往我只是简单的觉得自己只会老师教的知识,从来没有自己去学习课外的本领。嘴上虽然天天说,但是实际却从没有付之行动,在开始本次作业后,我深深的感觉到了无力,Git、GitHub desktop、单元测试、性能优化.....一眼看去基本上都不会,这才开始了新技术的学习,学习效率又很低,一开始一个Git就写了四五天,后面学习其他技术也是一样,最后时间很赶(很多步骤也没有按照正规流程完成),因此我认为本次作业对我最大的收获是为我敲醒了警钟,看清自己与好同学之间的差距,明白自己接下来以怎样的态度去学。同时我也在本次作业中收获了很多的新技术,了解了很多的新知识,明白只要去用功念是会掌握的,但一定要做好时间规划,不要一拖再拖最后压着时间线交。
与java后端相关的五个仓库
链接:https://github.com/codingXiaxw/CustomerManagement
简介:该仓库是一个客户管理系统,使用到的技术就是JSP+Servlet+Mysql,查询信息采取了分页技术
链接:https://github.com/macrozheng/mall
简介:该项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。
链接:https://github.com/lenve/vhr
简介:改系统是一个前后端分离的人力资源管理系统,项目采用 SpringBoot + Vue 开发。项目打通了前后端,并且提供了较为的文档,从 Spring Boot 接口设计到前端 Vue 的开发思路,作者全部都记录在项目的 wiki 中。
链接:https://link.zhihu.com/?target=https%3A//github.com/C0de8ug/Javaee-tutorial
简介:该仓库是一个简易的教务管理系统(并且集合了图书采购功能,教师可以将自己需要的图书添加之后通过审批,之后采购部可以看到需要采购的图书).该系统使用到了Springmvc框架作为MVC分层,使用Maven作为项目管理工具。
链接:https://github.com/ityouknow/spring-boot-examples
简介:这个项目中整合了 Spring Boot 使用的各种示例,此开源项目中的每个示例都以最小依赖,最简单为标准,帮助初学者快速掌握 Spring Boot 各组件的使用。基本上涉及到了 Spring Boot 使用的方方面面。