寒假作业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:

      test1

    • 缺少必要的log参数

      java InfectStatistic list -out D:\

      test2

    • 测试-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

      test3

      out1

    • 测试-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 福建 河北

      test4

      out2

    • 测试-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 全国 浙江 福建

      test5

      out3

    • 日期超出范围

      java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out5.txt -date 2020-02-23

      test6

    • 测试只有-log、-out参数,统计全部数据

      java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out4.txt

      test7

      out4

    • 测试相同-out参数即目标文件已存在

      java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out4.txt

      test8

    • 测试一个合法但不存在日志的日期

      java InfectStatistic list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\Out5.txt -date 2020-01-25

      test9

      out5

    8.单元测试覆盖率优化和性能测试,性能优化截图和描述。

    • 覆盖率测试

      list -log D:\Java\InfectStatistic-main\example\log -out D:\Java\InfectStatistic-main\example\result\out6.txt -date 2020-01-27

      coverresult

      分析:

      • FileHandleTool覆盖较少主要是因为产生输出的时候,受province和type参数影响,产生了四个判断分支,因此覆盖较少
      • CommandGet覆盖率不高主要是因为缺少province和type参数,因此提取参数时很多判断没有覆盖到
      • DateCompareTool主要是因为获取待处理文件时,非法处理较多,因此影响了覆盖率
    • 性能测试

      xingnengtest

      • 程序中,较多地用到了ArrayList,其底层用数组实现,故char[]使用频率很高。
      • 程序中嵌套较多,性能会比较差一些
      • 频繁使用到切割字符串存贮比较,会影响耗时
    • 优化

      暂时没有什么好的优化方案。

    9.代码规范的链接

    代码规范

    10.心路历程与收获

    ​ 真的刚要开始准备做这次作业的时候,内心世界是崩溃的。开始在心里觉得这次作业涉及面很广,感觉知识点又很多,整体很杂乱。再加之自己之前也没有github的使用经验。所以一开始从在哪里着手都不知道。之后好在助教发了一篇关于此次作业的一些提示和引导。于是我就从github的学习着手,一步一步学习使用各项功能,之后看懂了示例文件的结构,并成功地fork进了我的仓库和本地。至于之后的编码工作其实算法上没有什么难度,只是功能比较繁琐。

    ​ 其实当我真正沉下心来开始一步步分析学习这次作业,也只是用了4个小时左右,就从毫无思绪的状态,到了感觉思路很明确各项功能门清的境地了。感觉心里已经有底了,之后就开始沉下心来设计、编码、测试、改bug。

    ​ 通过这次作业我感觉我的收获有一个是复习了Java设计,当初学Java的时候,个人还是很喜欢Java语言的编程风格的,只是后来疏于联系,很多语法都不是那么熟练,这次作业让我将忘掉的语法又捡了起来。

    ​ 其次就是学会了github进行代码管理,这真的是个很棒也很方便的平台,刚开始没有使用过的时候感觉很多功能都很繁杂,但熟悉之后就觉得他很便利也很简明。

    ​ 再次就是了解学会了Eclipse的覆盖率测试和利用JProfiler进行性能测试。

    ​ 最后,最重要的还是树立了信心吧。虽然我在软件工程和计算机方面真的不是很有灵性,但是所有的事情只要静下心去做,还是可以摸到门路,最后基本完成任务的!!!

    11.相关的仓库

posted @ 2020-02-16 08:17  是九啊  阅读(347)  评论(2编辑  收藏  举报