zoukankan      html  css  js  c++  java
  • 软工实践寒假作业(2/2)

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

    格式描述

    这个作业属于哪个课程 2020春|S班 (福州大学)
    这个作业要求在哪里 作业要求的链接
    这个作业的目标 开发疫情统计程序,并借此熟悉程序开发流程和github的使用
    作业正文 ....
    其他参考文献 知乎、博客园

    github仓库地址:https://github.com/bzzd2333/InfectStatistic-main

    1、PSP表格

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

    2、思路描述

      我看到这个题目后,决定将程序分为命令初始化、处理日志、输出日志三个部分。使用的数据结构为:HashMap<String,Province>,其中String为省份的名称,Province为一个表示省份详细情况的类。省份的名称由一个List<String>存放。

      处理日志思路如下:日志每一行的结构如下图,只要判断array[1]、array[2]的值就可以知道所有情况并处理。


      输出日志就是正常输出到文件没什么特别的思路。

    3、设计实现过程

      我的程序模块结构图:


      我的关键函数流程图:

    4、代码说明

    • 命令行初始化:

        其中date为指定的日期,logPath、outputPath分别为日志目录的位置与输出文件的路径与文件名。dealType函数将-type的参数:要输出的人员情况存入output数组。dealProvince函数将-province的参数:指定列出的省份存入List<String>provinces。

        public InfectStatistic(String[] args)
        {
        	/*
        	类的成员变量的初始化。
        	*/
            //对命令行参数的初始化
        	this.init();
        }
        //初始化
        public void init()
        {
        	for(int i=0;i<arg.length;i++)
        	{			
        		switch(arg[i])
        		{
        		    case "-date":
        			    date = new String(arg[i+1]);
        			    isRead = false;	
        			    break;
        		    case "-log":
        	            logPath = new String(arg[i+1]);
        			    break;
        		    case "-out":
            		    outputPath = new String(arg[i+1]);
        			    break;
        		    case "-type":
        		    	isOutput = false;
        		    	dealType(i+1);
        		    	break;
        			case "-province":
                    	isOutputAll = false;
        		    	dealProvince(i+1);
        			default:	
        				break;
                }			
            }
        }
        //处理-type参数
        public void dealType(int index)
        {		
        	for(int i=0;index<arg.length && i<4;i++)
        	{
        		switch(arg[index])
        		{
        		    case "ip":
        		    	output[i] = arg[index];
        		    	break;
        		    case "sp":
        		    	output[i] = arg[index];
        		    	break;
        		    case "cure":
        		    	output[i] = arg[index];
        		    	break;
        		    case "dead":
        		    	output[i] = arg[index];
        		    	break;
        		    default:
        		    	break;
        		}
        		index++;
        	}
        }	
        //处理-province参数
        private void dealProvince(int index)
        {
        	while(index<arg.length)
        	{
        		switch(arg[index])
        		{
        		case "-date":
        		    return;
        	    case "-log":
        		    return;
        	    case "-out":
        		    return;
        	    case "-type":
        	    	return;
        	    default:
        	    	provinces.add(arg[index]);
        	    	map.put(arg[index],new Province(arg[index]));
        		}	
        		index++;
        	}				
        }		
      
    • 处理日志:

        deal函数处理所有日志文件,dealOneLine函数处理日志文件中的一行。

        public void deal() throws IOException
        {	    
        	String logDate;		
        	String[] sArray;
            File file = new File(logPath);
            File[] tempList = file.listFiles();
            
            /*
        	判断-date提供的日期是否晚于日志最晚一天的日期
        	*/
            
            //读取日志文件
            for (int i = 0; i < tempList.length; i++) 	                
            {     	
                          
                logDate = new String(tempList[i].getName());	                  
                sArray = logDate.split("\.");	                  
                logDate = new String(sArray[0]);
                 
        		//读取的日志日期小于指定的日期													
                if (isRead || (logDate.compareTo(date)) <= 0) 	                   
                {	              
                	BufferedReader br = null;	        	
                	String line = null;        	
                	br = new BufferedReader(new InputStreamReader(new FileInputStream(tempList[i].toString()), "UTF-8"));  
                    
                	while((line = br.readLine()) != null)
                	{
                		String[] array = line.split(" ");
                		//处理单行
                		dealOneLine(array);
                	}	       
                	br.close();
                }            	                
            }
            //统计全国的情况
            allStatistic();
        }	
        //处理单行
        private void dealOneLine(String[] array) 
        {
            //忽略注释行
            if (array[0].equals("//"))
            {
                return;
            }
            //处理的省份未初始化
            if (map.get(array[0]) == null)
            {
                map.put(array[0], new Province(array[0]));  
            }
                            
            switch (array[1])
            {
                case "新增":
                    if (array[2].equals("疑似患者"))
                    {
                        map.get(array[0]).addSp(array[3]);
                    }
                    else
                    {
                        map.get(array[0]).addIp(array[3]);
                    }
                    break;
                case "感染患者":
                    //流出省份减少感染患者
                    map.get(array[0]).removeIp(array[4]);
                    //流入省份增加感染患者
                    if (map.get(array[3]) == null)
                        map.put(array[3], new Province(array[3]));
                    map.get(array[3]).addIp(array[4]);
                    break;
                case "疑似患者":
                    if (array[2].equals("流入"))
                    {
                        //流出省份减少疑似患者
                        map.get(array[0]).removeSp(array[4]);
                        //流入省份增加疑似患者
                        if (map.get(array[3]) == null)
                            map.put(array[3], new Province(array[3]));
                        map.get(array[3]).addSp(array[4]);
                    }
                    //疑似患者确诊感染
                    else
                    {
                        //感染患者增加
                        map.get(array[0]).addIp(array[3]);
                        //疑似患者减少
                        map.get(array[0]).removeSp(array[3]);
                    }
                    break;
                case "死亡":
                    map.get(array[0]).dead(array[2]);
                    break;
                case "治愈":
                    map.get(array[0]).cure(array[2]);
                    break;
                case "排除":
                    map.get(array[0]).removeSp(array[3]);
                    break;
                default:
                    break;
            }   
        }
        //统计全国的情况
        public void allStatistic()
        {
            for (int i = 0; i < name.size(); i ++ )
            {
                if (map.get(name.get(i)) != null)
                {
                    country.allAdd(map.get(name.get(i)));
                }            
            }
        }    
      
    • 日志输出:

        InfectStatistic类中的output方法用于输出所有要输出的省份到输出文件。Province类中的output方法用于输出单个省份的情况。List<String>name存放所有省份的名称,List<String>provinces存放-province参数指定的省份的名称。

        在InfectStatistic类中的output方法里,若boolean变量isOutputAll为true,即默认情况,则输出所有省份,遍历List<String>name;否则输出-province参数指定的省份,遍历存放所有省份的String数组provinceName,逐个判断其是否为-province参数指定的省份,若是则输出。

        在Province类中的output方法里,若boolean变量isOutput为true,即默认情况,则输出该省份四种人群的数量;否则按照output数组中指定人员类型进行输出。

        //InfectStatistic类中的output方法
        public void output() throws IOException
        {
            if (isFinish)
            {
                return;
            }
            BufferedWriter bw = new BufferedWriter(new FileWriter(outputPath)) ;
            //默认情况,没有-province参数
            if (isOutputAll)
            {
                country.output(isOutput,output,bw);
                for (int i = 0; i < name.size(); i ++ )
                {   
                    if (map.get(name.get(i)) != null)
                    {
                        map.get(name.get(i)).output(isOutput, output, bw);
                    }                
                }
            }
            //有-province参数
            else
            {
                if (provinces.contains("全国"))
                {
                    country.output(isOutput,output,bw);
                }
                for (int i = 0; i < provinceName.length; i ++ )
                { 
                    if (provinces.contains(provinceName[i]))
                    {
                        if (map.get(name.get(i)) == null)
                        {
                            map.put(provinceName[i], new Province(provinceName[i]));
                        }
                        map.get(provinceName[i]).output(isOutput, output, bw);
                    }                
                }
            }
            bw.write("// 该文档并非真实数据,仅供测试使用");
            bw.close();
        }    
        
        //Province类中的output方法
        public void output(boolean isOutput,String[] output,BufferedWriter bw) throws IOException
        {
        	//默认输出
        	if(isOutput)
        	{
        		bw.write(name + " 感染患者 " + infectionPatients + "人 " +
                        "疑似患者 " + suspectedPatients + "人 " +
                          "治愈 " + cure + "人 " +
                        "死亡 " + dead + "人");
        		bw.newLine();
        	}
        	//有-type参数情况下的输出
        	else
        	{
        		bw.write(name);
        		for(int i=0;i<4;i++)
        		{
        			switch(output[i])
        			{
        			    case "ip":			    	
        			    	bw.write(" 感染患者 " + infectionPatients + "人");	    	
        			    	break;
        		        case "sp":			    	
        		        	bw.write(" 疑似患者 " + suspectedPatients + "人");		    	
        		        	break;
        		        case "cure":			    	
        		        	bw.write(" 治愈 " + cure + "人");			    	
        		        	break;
        		        case "dead":			     	
        		        	bw.write(" 死亡 " + dead + "人");	    	
        		        	break;
        		        default:		    	
        		        	break;
        			}
        		}
        		bw.newLine();
        	}		
        }	
      

    5、单元测试截图和描述

      本次单元测试读取的日志为example下的三个log.txt文件

      单元测试1:测试默认情况下,即没有参数-date、-type、-province的结果。输出为所提供日志最新的一天所有省份的情况:


      单元测试2:测试参数-date为2020-01-23的情况,此时程序需要处理日志:2020-01-22.log.txt、2020-01-23.log.txt,输出为1月23日所有省份的情况:


      单元测试3:测试参数-date为2020-01-25的情况,因为没有日志2020-01-24.log.txt,所以此时程序只需要处理日志:2020-01-22.log.txt、2020-01-23.log.txt,输出为1月25日所有省份的情况:


      单元测试4:测试参数-date为2020-01-27,-type为ip的情况,输出为1月27日所有省份的感染患者的人数:


      单元测试5:测试参数-date为2020-01-27,-type为ip,sp的情况,输出为1月27日所有省份的感染患者与疑似患者的人数:


      单元测试6:测试参数-date为2020-01-27,-type为ip,cure,dead,sp的情况,按顺序输出1月27日所有省份的感染患者、治愈、死亡、疑似患者的人数:


      单元测试7:测试参数-date为2020-01-27,-type为ip,-province为“福建”,“全国”的情况,因为全国总是排在第一个,所以按顺序输出1月27日全国、福建的感染患者的人数:


      单元测试8:测试参数-date为2020-01-27,-province为“福建”,“全国”,“北京”的情况,其中因为输入文档没有涉及到北京,所以北京的各项数据均为0,且因为全国总是排在第一个,且按拼音排序北京应该在福建的前面,所以按顺序输出1月27日全国、北京、福建的所有人数:


      单元测试9:测试参数-province为“福建”,“全国”,“北京”,-date为2020-01-27的情况,因为命令行参数的顺序可以调换,所以单元测试9的结果与单元测试8一样


      单元测试10:测试参数-date为2020-01-29的情况,因为最新一天的日志为2020-01-27.log.txt,所以应给与“日期超出范围”的错误提示。


    6、单元测试覆盖率优化和性能测试

    单元测试覆盖率如下:

    因为程序中未覆盖到的代码都为一些错误命令的判断,所以没有什么要优化的地方。

    性能如下:


    7、代码规范链接

      代码规范链接:https://github.com/bzzd2333/InfectStatistic-main/blob/new/221701236/codestyle.md

    8、心路历程与收获

      在本次的程序开发中,首先我明白了程序的开发并不只有编写程序,从一开始的需求分析到最后的测试,每个环节都大有门道;其次,我学习了很多新东西,如github、单元测试、JProfiler的使用,这让我明白了持续学习的重要性;第三,通过分析PSP表格,我发现我在具体编码上多花了很多时间,这也是因为我的粗心大意以及对需求分析不足所导致的,所以我以后应该更加仔细地分析需求。最后,本次的作业使我受益良多,我相信这次作业学到的东西和积累的经验必可活用于下次。

    9、技术路线图相关的5个仓库

      1、JavaGuide
        链接:https://github.com/bzzd2333/JavaGuide
        简介:该仓库总结了JAVA的学习路线及知识点和面试技巧,适合进行JAVA知识点的复习。
      2、SpringAll
        链接:https://github.com/bzzd2333/SpringAll
        简介:该仓库包括了Spring Boot,Spring Boot&Shiro,Spring Cloud,Spring Boot&Spring Security&Spring Security OAuth2等系列教程。
      3、JAVAWeb-Project
        链接:https://github.com/bzzd2333/JAVAWeb-Project
        简介:该仓库存放的是开始学习JAVA-WEB开发的一些练手项目,这些也适合初学者进行练习
      4、3y
        链接:https://github.com/bzzd2333/3y
        简介:该仓库从Java基础、JavaWeb基础到常用的框架再到面试题都有完整的教程,几乎涵盖了Java后端必备的知识点。
      5、JAVAEETest
        链接:https://github.com/bzzd2333/JavaEETest
        简介:该仓库存放了一些Spring、SpringMVC、MyBaits、Spring Boot案例。

  • 相关阅读:
    java 用代码实现判断字符串的开头和结尾
    java基础 1-path
    C#基础(语句 for循环)
    C#基础(数组)
    C#基础(语句 if else)
    C#基础(变量、常量、运算符)
    继承-person
    继承-字母表
    继承-monkey
    继承-people
  • 原文地址:https://www.cnblogs.com/bzzd/p/12310813.html
Copyright © 2011-2022 走看看