软工实践寒假作业(2/2)

这个作业属于哪个课程 班级的链接
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 学习Github的使用,设计一个疫情统计系统,学习PSP,学习《构建之法》,学习对程序进行优化
作业正文 https://edu.cnblogs.com/campus/fzu/2020SPRINGS/homework/10287
其他参考文献 知乎github使用教程知乎单元测试,《构建之法》,...

1.Github仓库地址

疫情统计系统

2.PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 45
Estimate 估计这个任务需要多少时间 30 45
Development 开发 910 960
Analysis 需求分析 (包括学习新技术) 90 150
Design Spec 生成设计文档 20 25
Design Review 设计复审 20 15
Coding Standard 代码规范 (为目前的开发制定合适的规范) 30 25
Design 具体设计 30 30
Coding 具体编码 600 540
Code Review 代码复审 30 25
Test 测试(自我测试,修改代码,提交修改) 90 150
Reporting 报告 60 75
Test Repor 测试报告 30 40
Size Measurement 计算工作量 15 25
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 15 10
合计 1000 1080

3.解题思路表述

首先我们来看需求,并进行分析。

需求:

1.通过命令行输入命令和参数。
2.根据输入参数的不同来选择性操作。
3.读取指定目录下的文件。
4.处理文件中的数据,并以指定格式、与文件名有序输出到文件。

4.设计实现过程

程序流程图

主要流程如下图所示,部分功能在图中已简化,在代码说明中将详细描述。

模块结构

输入的参数的存放
public static String inputPath = "";
	public static String outputPath = "";
	public static String [] provinceList = new String[]{"","","","","","","","","","","","","",""
			,"","","","","","","","","","","","","","","","","","","","",""};
	public static String [] typeList = new String[]{"","","",""};
	public static String dateString = "";
	public static Date inputDate = new Date();
	public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
	public static String initMaxDateString = "1900-01-01";
	public static String now = "";
	public static int nowIndex = 0;
Province类
public static class Province
	{
		int ip = 0;
		int sp = 0;
		int cure = 0;
		int dead = 0;
		String name = "";
		Province(String name)
		{
			this.ip = 0;
			this.sp = 0;
			this.cure = 0;
			this.dead = 0;
			this.name = name;
		}
	}
Province类数据修改方法
public static void ipIncrease(Province p, int num){...}
public static void spIncrease(Province p, int num){...}
public static void cureIncrease(Province p, int num){...}
public static void deadIncrease(Province p, int num){...}
public static void spToIp(Province p, int num){...}
public static void spDecrease(Province p, int num){...}
public static void ipMoveTo(Province p1, Province p2, int num){...}
public static void spMoveTo(Province p1, Province p2, int num){...}
main函数
public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		int number = args.length;
		Province nation = new Province("全国");
		HashMap<String,Province> map = new HashMap<>();
		if(args[0].equals("list"))
		{
			for(int i=1;i<number;i++)
			{
				getParameters(args[i]);
			}
		}
		else 
		{
			System.out.print("不存在该命令!");
			return ;
		}
		if(!Arrays.asList(args).contains("-log")||!Arrays.asList(args).contains("-out"))
		{
			System.out.print("缺少参数-log或-out");
			return ;
		}
		if(dateString.equals(""))
		{
			try 
			{
				inputDate = format.parse(format.format(getMaxDate(inputPath)));
			} 
			catch (ParseException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(!checkDate(inputPath,inputDate))
		{
			System.out.print("输入的日期超出范围!");
			return ;
		}
		else
		{
			map = readInfo(inputPath, inputDate, map);
			nation = calTotal(map);
		}
		Set set = map.keySet();
	    Object[] arr = set.toArray();
	    Comparator<Object> com = Collator.getInstance(java.util.Locale.CHINA);
	    Arrays.sort(arr);
	    Arrays.sort(provinceList,com);	    
	    outputInfo(typeList, provinceList, nation, outputPath, map, set);
	}

5.代码说明

读取命令行参数方法

思路:now为当前将要写入的参数名,nowIndex为当前将要写入的参数数组的下标,当读到非参数名的参数是,可通过now来判断其值对应的参数,这样可以实现参数顺序不定情况下的正确读取。

public static void getParameters(String s)
	{
		if(s.equals("-log"))
		{
			now = "log";
			nowIndex = 0;
		}
		else if(s.equals("-out"))
		{
			now = "out";
			nowIndex = 0;
		}
		else if(s.equals("-date"))
		{
			now = "date";
			nowIndex = 0;
		}
		else if(s.equals("-type"))
		{
			now = "type";
			nowIndex = 0;
		}
		else if(s.equals("-province"))
		{
			now = "province";
			nowIndex = 0;
		}
		else
		{
			if(now.equals("type"))
			{
				typeList[nowIndex] = s;
				nowIndex++;
			}
			else if(now.equals("province"))
			{
				provinceList[nowIndex] = s;
				nowIndex++;
			}
			else if(now.equals("log"))
			{
				inputPath = s;
			}
			else if(now.equals("out"))
			{
				outputPath = s;
			}
			else if(now.equals("date"))
			{
				try 
				{
					dateString = s;
					inputDate = format.parse(s);
				} 
				catch (ParseException e) 
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

读取文件方法

思路:

public static HashMap<String,Province> readInfo(String path, Date date, HashMap<String,Province> map)
	{
		String [] list = new File(path).list();
		int compareTo = 0;
		for(int i=0;i<list.length;i++)                
		{
			try 
			{
				Date current = format.parse(list[i].substring(0, 10));
				compareTo = date.compareTo(current);
				if (compareTo >= 0)
				{
					String filePath = path + list[i];
					File file = new File(filePath);
					FileInputStream fis = new FileInputStream(filePath);
					InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
					try (BufferedReader br = new BufferedReader(isr))
					{
				        String line = null;
				        String [] info = null;
				        String provinceName1 = null;
				        String provinceName2 = null;
				        int operateNum = 0;
				        while ((line = br.readLine()) != null)
				        {
				            line = line.trim();
				            info = line.split(" ");
				            Province p1 = null;
				            Province p2 = null;
				            if(info[0].substring(0, 2).equals("//"))
				            {
				            	break;
				            }
				            else if(info.length==3)
				            {
				            	provinceName1 = info[0];
				            	operateNum = Integer.parseInt(info[2].substring(0, info[2].length() - 1));
				            	if(map.containsKey(provinceName1))
			            		{
			            			p1 = map.get(provinceName1);
			            		}
			            		else
			            		{
			            			p1 = new Province(provinceName1);
			            		}
				            	if(info[1].equals("死亡"))
				            	{
				            		deadIncrease(p1, operateNum);
			            			map.put(provinceName1,p1);
				            	}
				            	else if(info[1].equals("治愈"))
				            	{
				            		cureIncrease(p1, operateNum);
			            			map.put(provinceName1,p1);
				            	}
				            }
				            else if(info.length==4)
				            {
				            	provinceName1 = info[0];
				            	operateNum = Integer.parseInt(info[3].substring(0, info[3].length() - 1));
				            	if(map.containsKey(provinceName1))
			            		{
			            			p1 = map.get(provinceName1);
			            		}
			            		else
			            		{
			            			p1 = new Province(provinceName1);
			            		}
				            	if(info[1].equals("新增"))
				            	{
				            		if(info[2].equals("感染患者"))
				            		{
				            			ipIncrease(p1, operateNum);
				            			map.put(provinceName1,p1);
				            		}
				            		else if(info[2].equals("疑似患者"))
				            		{
				            			spIncrease(p1, operateNum);
				            			map.put(provinceName1,p1);
				            		}
				            	}
				            	else if(info[1].equals("排除"))
				            	{
				            		spDecrease(p1, operateNum);
			            			map.put(provinceName1,p1);
				            	}
				            	else if(info[1].equals("疑似患者"))
				            	{
				            		spToIp(p1, operateNum);
				            		map.put(provinceName1,p1);
				            	}
				            }
				            else if(info.length==5)
				            {
				            	provinceName1 = info[0];
				            	provinceName2 = info[3];
				            	operateNum = Integer.parseInt(info[4].substring(0, info[4].length() - 1));
				            	if(map.containsKey(provinceName1))
			            		{
			            			p1 = map.get(provinceName1);
			            		}
			            		else
			            		{
			            			p1 = new Province(provinceName1);
			            		}
				            	if(map.containsKey(provinceName2))
			            		{
			            			p2 = map.get(provinceName2);
			            		}
			            		else
			            		{
			            			p2 = new Province(provinceName2);
			            		}
				            	if(info[1].equals("感染患者"))
				            	{
				            		ipMoveTo(p1, p2, operateNum);
				            		map.put(provinceName1,p1);
				            		map.put(provinceName2,p2);
				            	}
				            	else if(info[1].equals("疑似患者"))
			            		{
				            		spMoveTo(p1, p2, operateNum);
				            		map.put(provinceName1,p1);
				            		map.put(provinceName2,p2);
			            		}
				            }
				        }
				    } 
					catch (IOException e) 
					{
						e.printStackTrace();
					}
				}
			} 
			catch (ParseException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (UnsupportedEncodingException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return map;
	}

输出文件方法

思路:

public static void outputInfo (String[] typeList, String[] provinceList, Province nation, String path, HashMap<String,Province> map, Set set)
	{
		FileWriter fileWriter = null;
		String dirPath = "";
		if(path.indexOf("/")>1)
		{
			dirPath = path.substring(0,path.lastIndexOf("/"));
		}
		else 
		{
			dirPath = path.substring(0,path.lastIndexOf("/") + 1);
		}
		File dir = new File(dirPath);
		if (!dir.exists()) 
		{
	        dir.mkdirs();
	    }
		File file = new File(path);
		fileWriter = null;
		try 
		{
			if(!file.exists())
			{
				file.createNewFile();
			}
			else 
			{
				file.delete();
				file.createNewFile();
			}
			fileWriter = new FileWriter(file, true);
			if(provinceList[34].equals(""))       //输出全部省份信息
			{
				if(typeList[0].equals(""))
				{
					fileWriter.write(nation.name + " 感染患者"+nation.ip + "人 疑似患者"
						+nation.sp + "人 治愈" + nation.cure + "人 死亡"+nation.dead + "人\n");
				}
				else 
				{
					fileWriter.write(nation.name);
					for(int i=0;i<typeList.length&&!typeList[i].equals("");i++)
					{
						if(typeList[i].equals("ip"))
						{
							fileWriter.write(" 感染患者" + nation.ip + "人");
						}
						else if(typeList[i].equals("sp"))
						{
							fileWriter.write(" 疑似患者" + nation.sp + "人");
						}
						else if(typeList[i].equals("cure"))
						{
							fileWriter.write(" 治愈" + nation.cure + "人");
						}
						else if(typeList[i].equals("dead"))
						{
							fileWriter.write(" 死亡" + nation.dead + "人");
						}
					}
					fileWriter.write("\n");
				}
				Province p = null;
				for (Object str : set) 
				{  
					String name = (String)str;  
				 	p = map.get(name);
					if(typeList[0].equals(""))
					{
						fileWriter.write(p.name + " 感染患者" + p.ip + "人 疑似患者" 
							+ p.sp + "人 治愈" + p.cure + "人 死亡" + p.dead+"人\n");
					}
					else 
					{
						fileWriter.write(p.name);
						for(int i=0;i<typeList.length&&!typeList[0].equals("");i++)
						{
							if(typeList[i].equals("ip"))
							{
								fileWriter.write(" 感染患者" + p.ip + "人");
							}
							else if(typeList[i].equals("sp"))
							{
								fileWriter.write(" 疑似患者" + p.sp + "人");
							}
							else if(typeList[i].equals("cure"))
							{
								fileWriter.write(" 治愈" + p.cure + "人");
							}
							else if(typeList[i].equals("dead"))
							{
								fileWriter.write(" 死亡" + p.dead + "人");
							}
						}
						fileWriter.write("\n");
					}
				} 
			}
			else               //输出部分省份信息
			{
				if(Arrays.asList(provinceList).contains("全国"))
				{
					if(typeList[0].equals(""))
					{
						fileWriter.write(nation.name + " 感染患者" + nation.ip + "人 疑似患者"
							+ nation.sp+"人 治愈" + nation.cure + "人 死亡" + nation.dead + "人\n");
					}
					else 
					{
						fileWriter.write(nation.name);
						for(int j=0;j<typeList.length&&!typeList[j].equals("");j++)
						{
							if(typeList[j].equals("ip"))
							{
								fileWriter.write(" 感染患者" + nation.ip + "人");
							}
							else if(typeList[j].equals("sp"))
							{
								fileWriter.write(" 疑似患者" + nation.sp + "人");
							}
							else if(typeList[j].equals("cure"))
							{
								fileWriter.write(" 治愈" + nation.cure + "人");
							}
							else if(typeList[j].equals("dead"))
							{
								fileWriter.write(" 死亡" + nation.dead + "人");
							}
						}
						fileWriter.write("\n");
					}
				}
				for(int i=0;i<provinceList.length&&!provinceList[34].equals("");i++)
				{
					if(provinceList[i].equals("全国"))
					{
						continue;
					}
					else if(!map.containsKey(provinceList[i])&&!provinceList[i].equals(""))
					{
						if(typeList[0].equals(""))
						{
							fileWriter.write(provinceList[i] + " 感染患者0人 疑似患者0人 治愈0人 死亡0人\n");
						}
						else 
						{
							fileWriter.write(provinceList[i]);
							for(int k=0;k<typeList.length&&!typeList[0].equals("");k++)
							{
								if(typeList[k].equals("ip"))
								{
									fileWriter.write(" 感染患者0人");
								}
								else if(typeList[k].equals("sp"))
								{
									fileWriter.write(" 疑似患者0人");
								}
								else if(typeList[k].equals("cure"))
								{
									fileWriter.write(" 治愈0人");
								}
								else if(typeList[k].equals("dead"))
								{
									fileWriter.write(" 死亡0人");
								}
							}
							fileWriter.write("\n");
						}
					}
					else 
					{
						if(provinceList[i].equals(""))
						{
							continue;
						}
						Province p = null;
						String name = provinceList[i];  
						p = map.get(name);
						if(typeList[0].equals(""))
						{
							fileWriter.write(p.name + " 感染患者" + p.ip+"人 疑似患者" 
								+ p.sp + "人 治愈" + p.cure + "人 死亡" + p.dead + "人\n");
						}
						else 
						{
							fileWriter.write(p.name);
							for(int k=0;k<typeList.length&&!typeList[0].equals("");k++)
							{
								if(typeList[k].equals("ip"))
								{
									fileWriter.write(" 感染患者" + p.ip + "人");
								}
								else if(typeList[k].equals("sp"))
								{
									fileWriter.write(" 疑似患者" + p.sp + "人");
								}
								else if(typeList[k].equals("cure"))
								{
									fileWriter.write(" 治愈" + p.cure + "人");
								}
								else if(typeList[k].equals("dead"))
								{
									fileWriter.write(" 死亡" + p.dead + "人");
								}
							}
							fileWriter.write("\n");
						}
					}
				}
			}
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		finally 
		{
			try 
			{
				fileWriter.write("// 该文档并非真实数据,仅供测试使用\n");
				fileWriter.flush();
				fileWriter.close();
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
		}
	}

6.单元测试截图和描述

测试1

测试2

测试3

测试4

测试5

测试6

测试7

测试8

测试9

测试10

7.单元测试覆盖率优化和性能测试

以下为单元测试覆盖率性能测试
文件输出方法的性能非常低,我是不意外的,因为前期构思的问题,导致输出文件仅由一个方法来实现,而本程序本身就是对数据整理,并选择性输出,单靠一个方法实现这个功能,性能低时情理之中,这也是我在本次作业中应该反思的地方,提高性能需要把这个方法拆分开来。

另外,在《构建之法》中提到一点,让我印象深刻:“100%的代码覆盖率并不等同于100%的正确性”
确实如此,100%的代码覆盖率代表着每一次运行,每一行代码都被完美运行,这表示着不存在没有达到的分支。但是在程序运行时,参数存在各种各样的可能,不可能每次运行所有的方法代码覆盖率都达到100%,如果如此,这反而说明考虑到的情况不够多,存在隐含的bug。因此,不能一昧追求极高的代码覆盖率,在保证能对多种情况进行操作的前提下,合理提升代码覆盖率才是提高程序效率的王道。

8.代码规范

代码规范

9.心路历程和收获

这次的作业真是让我的心情感到非常复杂。由于自己编程基础不够扎实,对自己的编程技术也不是很有信心,看到作业就手忙脚乱,头脑一片乱麻,总是很难静下心来慢慢整理思路,甚至有点惧怕编程。但是作业还是要做的,经过煎熬的挣扎还是开始了学习、分析需求、组织思路、设计、编程......其实自己还是可以做出来的嘛,希望今后对自己更有信心。
经过这次作业,巩固了java的知识,也学习到Github的使用方法,以及PSP表格的绘制......同时在本次作业的反思中吸取到了深刻的教训!回顾本次作业的具体代码,发现许多方法长度不够精简,导致编程时时思路混乱、复审时思路不清,应该在设计时对方法进行总结概括,将具有类似或重复步骤的代码段单独封装成新的方法,时程序更加精简,可读性更强。
说要图文并茂,但是我觉得,用文字记录自己的心路历程就已足够,收获应该记在脑子里,硬要加一张图片满足需求意义不大。

10.技术路线图相关的5个仓库

1.Java知识学习及面试指南
https://github.com/Snailclimb/JavaGuide
2.轻量级简洁MVC框架
https://github.com/lets-blade/blade
3.包括了Spring Boot,Spring Boot&Shiro,Spring Cloud,Spring Boot&Spring Security&Spring Security OAuth2等系列教程。
https://github.com/wuyouzhuguli/SpringAll
4.Apache Flink 声明式的数据分析开源系统,结合了分布式 MapReduce 类平台的高效,灵活的编程和扩展性。同时在并行数据库发现查询优化方案
https://github.com/apache/flink
5.数据结构与算法
https://github.com/phishman3579/java-algorithms-implementation

posted @ 2020-02-21 02:15  汤姆不理不  阅读(171)  评论(1编辑  收藏  举报