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

别等时光非礼了梦想. 提交于 2020-02-15 11:11:49

软工实践寒假作业(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、心路历程与收获

  

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!