寒假作業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.相關的倉庫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM