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

这个作业属于哪个课程 班级链接
这个作业要求在哪里 作业要求
这个作业的目标 完成项目并编写博客,学习使用github
作业正文 正文在这里
其他参考文献 百度, 博客园, csdn

疫情分析

设计思路

三思而后行

将输入和输出分开处理.
在输入时, 循环读入日志文件中的每一行, 并按顺序解析每行的据,将其储存在类的私有变量中. 而输出时, 只需读取私有变量, 按要求写入文件即可
因此, 使用Map来存储每个省的信息. 解析时将省名为key, 省的四种情况构成一个新的Map作为值.

项目代码

文件解析部分

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
String textLine = null;
while ((textLine = bufferedReader.readLine())!=null) {
    if (textLine.contains("//"))
        continue;
......

使用输入流BufferedReader来读取项目下的文件, 在读取行时注意是否带有//标记. 有则直接跳过该行

String[] params = textLine.split(" ");
String province = params[0];
if (!infectMap.containsKey(province)) {
    HashMap<String, Integer> map = new HashMap<>();
    map.put("感染患者", 0);
    map.put("疑似患者", 0);
    map.put("治愈", 0);
    map.put("死亡", 0);
    infectMap.put(province, map);
}
......

对读到的一行, 将其分为String数组. 通过判断数组的第一个元素也就是该行所代表的省份, 可以判断是否应该直接生成一个新的map, 或是直接更新原有的map.

if (textLine.contains("新增")) {
    //新增人数
    count = infectMap.get(province).get(params[2]) + Integer.valueOf(params[3].replaceAll("人", ""));
    infectMap.get(province).replace(params[2], count);
} else if (textLine.contains("流入")) {
    //流出省份减少人数
    count = infectMap.get(province).get(params[1]) - Integer.valueOf(params[4].replaceAll("人", ""));
    infectMap.get(province).replace(params[1], count);

    //流入省份增加人数
    count = infectMap.get(params[3]).get(params[1]) + Integer.valueOf(params[4].replaceAll("人", ""));
    infectMap.get(params[3]).replace(params[1], count);
} else if (textLine.contains("确诊感染")) {
    //感染者人数增加
    count = infectMap.get(province).get("感染患者") + Integer.valueOf(params[3].replaceAll("人", ""));
    infectMap.get(province).replace("感染患者", count);

    //疑似患者人数减少
    count = infectMap.get(province).get("疑似患者") - Integer.valueOf(params[3].replaceAll("人", ""));
    infectMap.get(province).replace("疑似患者", count);
} else if (textLine.contains("排除")) {
    //疑似患者人数减少
    count = infectMap.get(province).get("疑似患者") - Integer.valueOf(params[3].replaceAll("人", ""));
    infectMap.get(province).replace("疑似患者", count);
} else { //治愈 死亡
    //感染者人数减少
    count = infectMap.get(province).get("感染患者") - Integer.valueOf(params[2].replaceAll("人", ""));
    infectMap.get(province).replace("感染患者", count);
    count = infectMap.get(province).get(params[1]) + Integer.valueOf(params[2].replaceAll("人", ""));
    infectMap.get(province).replace(params[1], count);
}
......

判断每个参数来更新map的内容

//修正全国人数变化
int ip = 0, sp = 0, cure = 0, dead = 0;
for (Map.Entry<String, Map<String, Integer>> e : infectMap.entrySet()) {
    if (!e.getKey().equals("全国")) {
        for (Map.Entry<String, Integer> entry: e.getValue().entrySet()) {
            switch (entry.getKey()) {
                case "感染患者": ip += entry.getValue(); break;
                case "疑似患者": sp += entry.getValue(); break;
                case "治愈": cure += entry.getValue(); break;
                case "死亡": dead += entry.getValue(); break;
            }
        }
    }
}
infectMap.get("全国").replace("感染患者", ip);
infectMap.get("全国").replace("疑似患者", sp);
infectMap.get("全国").replace("治愈", cure);
infectMap.get("全国").replace("死亡", dead);

bufferedReader.close();

最后修正全国的人数变化, 一个文件就算解析完成了

控制台参数部分

int optNumber = 0;
if (args[0].equals("list")) {
    String input = null, output = null, date = null;
    List<String> typeList = new LinkedList<>(), provinceList = new LinkedList<>();
    while (optNumber < args.length) {
        if (args[optNumber].equals("-log")) {
            input = args[++optNumber];
        } else if (args[optNumber].equals("-out")) {
            output = args[++optNumber];
        } else if (args[optNumber].equals("-date")) {
            date = args[++optNumber];
        } else if (args[optNumber].equals("-type")) {
            String type = args[++optNumber];
            while (!type.contains("-")) {
                typeList.add(type);
                if (optNumber == args.length-1)
                    break;
                type = args[++optNumber];

            }
        } else if (args[optNumber].equals("-province")) {
            String province = args[++optNumber];
            while (!province.contains("-")) {
                provinceList.add(province);
                if (optNumber == args.length-1)
                    break;
                province = args[++optNumber];
            }
        } else {
            optNumber++;
        }
    }
    parseDirectory(input, date);
    output(output, typeList, provinceList);
}

遍历args数组, 判断并提取参数, 交给parseDirectory(解析目录下的所有文件)和output(输出)两个方法来处理

输出部分

File file = new File(path);
if (!file.exists()) {
    file.createNewFile();
}
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
List<String> results = parseType(typeList, provinceList);
StringBuilder stringBuilder = new StringBuilder();
String country = null;
//排序
Collator cmp = Collator.getInstance(Locale.CHINA);
results.sort(cmp);
for (String s : results) {
    if (s.contains("全国")) {
        country = s+"\n";
        continue;
    }
    stringBuilder.append(s+"\n");
}
String result = country + stringBuilder.toString();
bufferedWriter.write(result);
bufferedWriter.write("// 该文档并非真实数据,仅供测试使用");
bufferedWriter.close();

注意此方法每次写入相同文件会覆盖, 而不是新增

处理type和province参数部分

List<String> typeName = new LinkedList<>();
List<String> result = new LinkedList<>();
if (typeList.size() != 0) {
    for (String s:typeList) {
        switch (s) {
            case "ip": typeName.add("感染患者"); break;
            case "sp": typeName.add("疑似患者"); break;
            case "cure": typeName.add("治愈"); break;
            case "dead": typeName.add("死亡"); break;
        }
    }
} else {
    typeName.add("感染患者");
    typeName.add("疑似患者");
    typeName.add("治愈");
    typeName.add("死亡");
}
if (provinceList.size() != 0) {
    provinceList.add("全国");
    for (String province : provinceList) {
        StringBuilder sb = new StringBuilder();
        sb.append(province+ " ");
        if (infectMap.containsKey(province)) {
            for (String type : typeName) {
                sb.append(type+infectMap.get(province).get(type)+"人 ");
            }
        } else {
            for (String type : typeName) {
                sb.append(type+"0人 ");
            }
        }

        result.add(sb.toString());
    }
} else {
    for (Map.Entry<String, Map<String, Integer>> e : infectMap.entrySet()) {
        StringBuilder sb = new StringBuilder();
        sb.append(e.getKey()+" ");
        for (String type : typeName) {
            sb.append(type+e.getValue().get(type)+"人 ");
        }
        result.add(sb.toString());
    }
}
return result;

typeList中的参数简写ip sp dead cure转化为所对应的全称, 也是map中的key感染患者 疑似患者 死亡 治愈, 之后遍历map, 将符合province参数中的省份拼接, 最后添加到result这个List中返回, 由输出部分处理.

测试部分

测试均为单元测试, 输出忽略// 该文档并非真实数据,仅供测试使用

test1

list -log ./log -out ./result/out.txt

输出所有信息到result下的out.txt中, 文件内容如下

全国 感染患者149人 疑似患者324人 治愈27人 死亡21人 
福建 感染患者22人 疑似患者38人 治愈3人 死亡0人 
湖北 感染患者125人 疑似患者279人 治愈24人 死亡21人 
江苏 感染患者2人 疑似患者0人 治愈0人 死亡0人 
浙江 感染患者0人 疑似患者7人 治愈0人 死亡0人 

test2

list -log ./log -out ./result/out.txt -date 2020-01-23

输出2020-01-23以前的所有信息, 包括23日当日. 内容如下

全国 感染患者44人 疑似患者69人 治愈4人 死亡3人 
福建 感染患者9人 疑似患者16人 治愈1人 死亡0人 
湖北 感染患者33人 疑似患者46人 治愈3人 死亡3人 
江苏 感染患者2人 疑似患者0人 治愈0人 死亡0人 
浙江 感染患者0人 疑似患者7人 治愈0人 死亡0人 

test3

list -log ./log -out ./result/out.txt -date 2020-01-23 -province 福建 浙江

输出23日以前福建和浙江的信息

福建 感染患者9人 疑似患者16人 治愈1人 死亡0人 
浙江 感染患者0人 疑似患者7人 治愈0人 死亡0人 

test4

list -log ./log -out ./result/out.txt -date 2020-01-23 -province 福建 浙江 山东

输出23日以前福建,浙江,山东的信息(其中山东省的信息在日志中没有记载)

福建 感染患者9人 疑似患者16人 治愈1人 死亡0人 
山东 感染患者0人 疑似患者0人 治愈0人 死亡0人 
浙江 感染患者0人 疑似患者7人 治愈0人 死亡0人 

test5

list -log ./log -out ./result/out.txt -date 2020-01-23 -type ip sp dead

输出23日以前所有信息, 过滤掉治愈

全国 感染患者44人 疑似患者69人 死亡3人 
福建 感染患者9人 疑似患者16人 死亡0人 
湖北 感染患者33人 疑似患者46人 死亡3人 
江苏 感染患者2人 疑似患者0人 死亡0人 
浙江 感染患者0人 疑似患者7人 死亡0人 

test6

list -log ./log -out ./result/out.txt -province 福建 浙江 -type ip dead

输出福建和浙江感染患者和死亡人数

福建 感染患者22人 死亡0人 
浙江 感染患者0人 死亡0人 

test7

list -log ./log -out ./result/out.txt -province 福建 浙江 -type ip sp cure dead alive

输出福建和浙江的感染患者,疑似患者,治愈,死亡和存活(并不存在,应该忽略)

福建 感染患者22人 疑似患者38人 治愈3人 死亡0人 
浙江 感染患者0人 疑似患者7人 治愈0人 死亡0人

test8

list -log ./log -out ./result/out.txt -date 2020-01-01

输出2020-01-01以前的日志(空)

全国 感染患者0人 疑似患者0人 治愈0人 死亡0人

test9

list -log ./log -out ./result/out.txt -province 福建 浙江 江苏 -type ip sp cure

组合测试

福建 感染患者22人 疑似患者38人 治愈3人 
江苏 感染患者2人 疑似患者0人 治愈0人 
浙江 感染患者0人 疑似患者7人 治愈0人 

test10

list -log ./log -out ./result/out.txt -province 福建 浙江 江苏 西藏 -type sp cure

组合测试

福建 疑似患者38人 治愈3人 
江苏 疑似患者0人 治愈0人 
西藏 疑似患者0人 治愈0人 
浙江 疑似患者7人 治愈0人 

心路历程

此次作业, 首先让我再一次确定了我自己的代码规范, 进一步提高了我自己代码的可读性. 其次让我理解了先思考的重要性. 这样减少了很多代码的弯路. 最后, 我明白了资料的可靠性是非常重要的. 有些博客写的实例代码根本就不能跑, 在借鉴的时候需要擦亮双眼仔细甄别.

PSP

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

技术路线相关的仓库

  • spring-boot

    SpringBoot是由Pivotal团队在2013年开始研发、2014年4月发布第一个版本的全新开源的轻量级框架。它基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。
    其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域成为领导者。

  • mybatis

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object)映射成数据库中的记录。

  • redis

    Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。

  • shiro

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

  • junit4/junit5

    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试. 另外junit是在极限编程和重构(refactor)中被极力推荐使用的工具,因为在实现自动单元测试的情况下可以大大的提高开发的效率.

  • swagger

    Swagger 是一款RESTFUL接口的、基于YAML、JSON语言的文档在线自动生成、代码自动生成的工具。

项目地址

InfectionStatistic

代码规范

codestyle

posted @ 2020-02-13 16:05  lunacia  阅读(203)  评论(2编辑  收藏  举报