软件工程实践寒假作业(2/2)
这个作业属于哪个课程 | 2020春|W班 |
---|---|
这个作业要求在哪里 | 寒假作业(2/2)作业要求 |
这个作业的目标 | github使用 代码规范制定 疫情统计程序 博客撰写 |
作业正文 | 寒假作业(2/2) |
其他参考文献 |
Github仓库地址
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 20 |
Estimate | 估计这个任务需要多少时间 | 1000 | 1020 |
Development | 开发 | 800 | 720 |
Analysis | 需求分析 (包括学习新技术) | 60 | 50 |
Design Spec | 生成设计文档 | 60 | 40 |
Design Review | 设计复审 | 30 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 10 |
Design | 具体设计 | 60 | 30 |
Coding | 具体编码 | 420 | 450 |
Code Review | 代码复审 | 30 | 10 |
Test | 测试(自我测试,修改代码,提交修改) | 30 | 10 |
Reporting | 报告 | 60 | 30 |
Test Report | 测试报告 | 30 | 20 |
Size Measurement | 计算工作量 | 15 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 40 | 30 |
合计 | 2685 | 2490 |
解题思路
- 其实一开始拿到这个任务是很懵逼的,因为很久没接触Java了,突然就要我弄这么一个任务。对这个命令行其实我一开始有点一窍不通,这个命令行怎么和我写的Java程序交互呢?不同的命令能实现不同的效果?一开始是非常疑惑的。
- 然后我想了想最初学Java时是需要搭建环境并测试环境是否搭建成功的,需要用到的就是Javac和Java命令,然后Java的main方法里有个args的参数,进行百度之后就有了大致的了解,原来命令行的参数就是储存在args字符串数组中的,接下来就是对args中的参数进行解析分析,分析出具体是什么命令,然后用不同的类创建不同的方法进行实现,这就是我的大致思路。
- 然后后面还需要文件操作方面的知识,我就去百度查找了一些资料,然后就直接开始写了。
设计实现过程
- 一开始看了下要求,要求只能有InfectStatistic.java和Lib.java文件,所以我就在这个文件下进行改动,大致设计了三个类
- FileManager类负责文件的读取和输出,CommandIdentity类负责读取并获得命令的参数。大概的流程图是这样的:
关键代码说明
关键方法
CommandIdentity的Identity方法
public void Identify(){
int logpos=0,outpos=0,datepos=0,typepos=0,provincepos=0;
ArrayList<String > typeparam=new ArrayList<>();
ArrayList<String > provinceparam = new ArrayList<>();
String logparam="",outparam="",dateparam="";
if(!command[0].equals("list"))
System.out.println("命令格式错误!");
else{
/*log命令位置获取和参数*/
for(int i=0;i<command.length;i++) {
if (command[i].equals("-log")) {
logpos = i;
break;
}
}
logparam=command[logpos+1];
/*out命令位置获取和参数*/
for(int i=0;i<command.length;i++) {
if(command[i].equals("-out")) {
outpos = i;
break;
}
}
outparam=command[outpos+1];
/*date命令位置获取和参数*/
for(int i=0;i<command.length;i++) {
if (command[i].equals("-date")) {
datepos = i;
break;
}
}
if(command[datepos+1].startsWith("-"))
dateparam="";
else
dateparam=command[datepos+1];
/*type命令位置获取和参数*/
for(int i=0;i<command.length;i++) {
if (command[i].equals("-type")) {
typepos = i;
break;
}
}
for(int i=typepos+1;i<command.length;i++){
if(command[i].startsWith("-"))
break;
else
typeparam.add(command[i]);
}
/*province命令位置获取和参数*/
for(int i=0;i<command.length;i++) {
if (command[i].equals("-province")) {
provincepos = i;
break;
}
}
for(int i=provincepos+1;i<command.length;i++){
if(command[i].startsWith("-"))
break;
else
provinceparam.add(command[i]);
}
}
FileManager fm = new FileManager();
fm.ReadFile(logparam,dateparam);
fm.WriteFile(outparam,typeparam,provinceparam);
}
- 大致思路就是先找到各个命令的位置,然后再向后寻找该命令的参数,最后把找到的参数传给FileManager的ReadFile和WriteFile方法去具体处理。
FileManager的参数
class FileManager{
public String[] provinces={"安徽","北京","重庆","福建","甘肃","广东","广西","贵州",
"海南","河北","河南","黑龙江","湖北","湖南","吉林","江苏","江西","辽宁","内蒙古",
"宁夏","青海","山东","山西","陕西","上海","四川","天津","西藏","新疆","云南","浙江"};
public int[] InfectedPatients= new int[31];
public int[] SuspectedPatients=new int[31];
public int[] CurePatients=new int[31];
public int[] DeadPatients=new int[31];
public boolean[] IsVisited = new boolean[31];
public int AllIP=0,AllSP=0,AllCP=0,AllDP=0;
private static String REGEX_CHINESE = "[\u4e00-\u9fa5]";/* 中文正则*/
ArrayList<String> FileNames = new ArrayList<>();
ArrayList<String> FileContent = new ArrayList<>();
}
- 用4个整型数组来记录各省患者情况,用单独的4个整型变量来记录全国患者情况。IsVisited记录日志中记录的省,FileNames和FileContent分别记录日志文件名和日志的内容。
FileManager的ReadFile方法
public void ReadFile(String FilePath,String dateparam){
File f = new File(FilePath);
int stopPoint=0;/*判断读取哪些文件的断点*/
int flag=0;/*判断日期是否合法*/
String stopDate = dateparam + ".log.txt";
if(f.isDirectory()) {
String[] temp = f.list();
for(int i=0;i<temp.length;i++)
FileNames.add(temp[i]);
}
else
System.out.println("log文件目录错误!");
/*找到断点*/
if(dateparam.equals(""))
stopPoint=FileNames.size();
else{
for(int i=0;i<FileNames.size();i++){
if(FileNames.get(i).compareTo(stopDate) == 0){
stopPoint=i;
flag=1;
}
}
}
if(dateparam=="") {
flag = 1;
stopPoint=FileNames.size();
}
if(flag==0){
System.out.println("输入的日期超出范围!");
return;
}
try {
for(int i=0;i<=stopPoint;i++) {
String SingleFile = FilePath + "\\" + FileNames.get(i);
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(SingleFile),"UTF-8"));
String line = "";
while((line=br.readLine())!=null) {
if(!line.startsWith("/"))
FileContent.add(line);
}
}
}catch (Exception e){
}
GetStatistic(FileContent);
}
- 利用传进来的参数来判断该读取哪些文件,把要读取的文件都读取好,保存到FileContent中。
FileManager的GetStatistic和DealStrings方法
public void GetStatistic(ArrayList<String> FileContent){
for(int i=0;i<FileContent.size();i++){
String[] temp=FileContent.get(i).split(" ");
DealStrings(temp);
}
}
public void DealStrings(String[] str){
int index = 0;
int increase;
String tes=str[str.length-1];
/*除去中文字符*/
Pattern pat = Pattern.compile(REGEX_CHINESE);
Matcher mat = pat.matcher(tes);
increase=Integer.parseInt(mat.replaceAll(""));
for(int i=0;i<provinces.length;i++){
if(provinces[i].equals(str[0])){
index=i;
break;
}
}
if(str[1].equals("新增")){
if(str[2].equals("感染患者")){
InfectedPatients[index]+=increase;
AllIP+=increase;
}
else if(str[2].equals("疑似患者")){
SuspectedPatients[index]+=increase;
AllSP+=increase;
}
IsVisited[index]=true;
return;
}
else if(str[1].equals("感染患者")){
InfectedPatients[index]-=increase;
for(int i=0;i<provinces.length;i++){
if(provinces[i].equals(str[3])){
index=i;
break;
}
}
InfectedPatients[index]+=increase;
IsVisited[index]=true;
return;
}
else if(str[1].equals("疑似患者")){
if(str[2].equals("流入")){
SuspectedPatients[index]-=increase;
for(int i=0;i<provinces.length;i++){
if(provinces[i].equals(str[3])){
index=i;
break;
}
}
SuspectedPatients[index]+=increase;
IsVisited[index]=true;
return;
}
else if(str[2].equals("确诊感染")){
SuspectedPatients[index]-=increase;
InfectedPatients[index]+=increase;
AllSP-=increase;
AllIP+=increase;
IsVisited[index]=true;
return;
}
}
else if(str[1].equals("死亡")){
InfectedPatients[index]-=increase;
DeadPatients[index]+=increase;
AllIP-=increase;
AllDP+=increase;
IsVisited[index]=true;
return;
}
else if(str[1].equals("治愈")){
InfectedPatients[index]-=increase;
CurePatients[index]+=increase;
AllIP-=increase;
AllCP+=increase;
IsVisited[index]=true;
return;
}
else if(str[1].equals("排除")){
SuspectedPatients[index]-=increase;
AllSP-=increase;
IsVisited[index]=true;
return;
}
}
- 因为日志文件只会有8种情况,所以一种一种的进行判断,并根据情况增加或减少相应的患者数量即可。
FileManager的WriteFile方法
public void WriteFile(String FileName,ArrayList<String> typeparam,ArrayList<String> provinceparam){
File file = new File(FileName);
FileWriter fileWriter = null;
try {
if (!file.exists())
file.createNewFile();
fileWriter = new FileWriter(file, true);
if (provinceparam.size() == 0) {/*-province没有参数*/
String s = GetStringByType(typeparam,"全国");
fileWriter.write(s);
for (int i = 0; i < 31; i++) {
if (IsVisited[i]) {
String st = GetStringByType(typeparam,provinces[i]);
fileWriter.write(st);
}
}
}
if(provinceparam.size()!=0) {/*-province有参数*/
for(int i=0;i<provinceparam.size();i++){
String s=GetStringByType(typeparam,provinceparam.get(i));
fileWriter.write(s);
}
}
fileWriter.write("//该文档并非真实数据,仅供测试使用\n");
fileWriter.flush();
fileWriter.close();
}catch (Exception e){
}
finally {
try {
fileWriter.close();
}catch (Exception ee){
}
}
}
- 找到相应的文件,如果没有就创建,然后往这个文件里写入内容即可。
FileManager的GetStringByType方法
public String GetStringByType(ArrayList<String> type,String province){
String result="";
int index=0;
for(int i=0;i<31;i++){
if(province.equals(provinces[i]))
index=i;
}
result+=province + " ";
if(province.equals("全国")) {
if(type.size()!=0) {
for (int i = 0; i < type.size(); i++) {
if (type.get(i).equals("ip")) {
result += "感染患者" + AllIP + "人 ";
}
if (type.get(i).equals("sp")) {
result += "疑似患者" + AllSP + "人 ";
}
if (type.get(i).equals("cure")) {
result += "治愈" + AllCP + "人 ";
}
if (type.get(i).equals("dead")) {
result += "死亡" + AllDP + "人 ";
}
}
}
else{
result+=String.format("感染患者%d人 疑似患者%d人 治愈%d人 死亡%d人",
AllIP,AllSP,AllCP,AllDP);
}
return result + "\n";
}
if(type.size()==0){
result +=String.format("感染患者%d人 疑似患者%d人 治愈%d人 死亡%d人",
InfectedPatients[index], SuspectedPatients[index],
CurePatients[index], DeadPatients[index]);
}
else {
for (int i = 0; i < type.size(); i++) {
if (type.get(i).equals("ip")) {
result += "感染患者" + InfectedPatients[index] + "人 ";
}
if (type.get(i).equals("sp")) {
result += "疑似患者" + SuspectedPatients[index] + "人 ";
}
if (type.get(i).equals("cure")) {
result += "治愈" + CurePatients[index] + "人 ";
}
if (type.get(i).equals("dead")) {
result += "死亡" + DeadPatients[index] + "人 ";
}
}
}
return result + "\n";
}
- 这个函数的返回值就是往文件里写入的每一行,会对-province和-type的参数进行分析,没有参数,有参数,一共四种情况,具体情况具体分析,最后得到需要的字符串,并返回。
单元测试
测试-log -out -date参数
java InfectStatistic list -log E:\Projects\PPP\src\log\ -out E:\Projects\PPP\out\out1.txt -date 2020-01-22
结果
测试-log -out -date -province参数
java InfectStatistic list -log E:\Projects\PPP\src\log\ -out E:\Projects\PPP\out\out2.txt -date 2020-01-22 -province 福建 河北
测试-log -out -date -province -type参数
java InfectStatistic list -log E:\Projects\PPP\src\log\ -out E:\Projects\PPP\out\out3.txt -date 2020-01-23 -type cure dead ip -province 全国 浙江 福建
测试-date参数超出范围
java InfectStatistic list -log E:\Projects\PPP\src\log\ -out E:\Projects\PPP\out\out3.txt -date 2020-01-28
测试-log参数错误
java InfectStatistic list -log E:\log\ -out E:\Projects\PPP\out\out3.txt -date 2020-01-22
测试命令格式错误
java InfectStatistic li -log E:\log\ -out E:\Projects\PPP\out\out3.txt -date 2020-01-22
单元测试覆盖率优化和性能测试,性能优化截图和描述
覆盖率测试
java InfectStatistic list -log E:\Projects\PPP\src\log\ -out E:\Projects\PPP\out\out3.txt -date 2020-01-23 -type cure dead ip -province 全国 浙江 福建
分析:收到-province和-type参数的影响,产生了不同的分支,使得覆盖率有所下降。
性能测试方面:由于Jprofiler网站正在维护,无法下载,不知道如何测试,就先放着吧
代码规范链接
心路历程与收获
- 刚开始作业公布时,只是大致地瞥了几眼,觉得这次作业好像还蛮贴近时事的,只是有这个想法而已。而当自己上手真正地看了几遍要求后就觉得这个作业并不简单,需要很多的新技术,而我都不太会,所以我当时很懵逼,要一步一步地走,慢慢的学习新知识并进行运用,这对于我这个急性子简直是太难了。最开始是非常艰难的几天,新知识很难接受,要慢慢地自己消化吸收,当时我觉得这个作业太难了,怎么可能完成得了。
- 再后来,经过几天的学习,我慢慢地进入了学习的状态,查找资料、学习新技术都得心应手,慢慢地开发出了一个功能越来越完善的项目,这其中慢慢填充起来的满足感估计只有一步一步走过来的人才能感受到,当我基本上完成了所有的作业时,心情是非常愉悦的,感觉一块大石头落地了,很开心。
- 这次作业我收获很多,既学到了github的使用,又捡起了很久没用的Java技术,并且又增加了新的知识,可以说是收获颇丰,并且开发完一个项目的满足感真的能让人开心很久,这段时间也很充实。
相关的仓库
- GitHub上最火的Android开源项目,所有开源项目都有详细资料和配套视频
- 收集Android方方面面的经典知识, 最新技术。
- AndroidUtilCode是一个强大易用的安卓工具类库,它合理地封装了安卓开发中常用的函数,具有完善的 Demo 和单元测试,利用其封装好的 APIs 可以大大提高开发效率
- 作者微博: @GcsSloop 的安卓学习笔记,记录学习过程中遇到的问题,以及我的一些经验总结。
- 收集Android开发中优秀的开源组件库,方便大家查询,欢迎 PR 和 star~