软工实践2020寒假作业(2/2)
软工实践2020寒假作业(2/2)
| 作业属于 | 2020软工实践W班 |
|---|---|
| 作业要求 | 作业要求 |
| 作业目的 | 熟悉Github,完成疫情统计小程序 |
| 作业正文 | 如下 |
| 其他参考文献 | 作业引导,Git教程,单元测试教程 |
| Github仓库地址 | 仓库地址 |
- Notice: deline 2020-02-18 23:00
一.阅读《构建之法》
第一章 概论
对于构建之法第一章的概论,有几句话我认为很有道理
- 程序 = 数据结构 + 算法
- 软件 = 程序 + 软件工程
- 软件企业 = 软件 + 商业模式
记得大一刚接触编程,那时候的软件概念在我看来就是一堆数据加上编程语言的应用构建出来的东西,后来学多了逐渐就出现了程序什么的专业名词,有一段时间我甚至认为程序就是软件,软件就是程序。再后来学习中,我慢慢发现这两者并不是一个东西,程序是由一段代码组成,其中的数据也有相应的结构,我们想要处理数据也要设计相应的算法,而程序要经过软件工程的加工才能扩展为一个满足各个功能的应用软件。《构建之法》概论帮助我更好的分清什么是软件,什么是软件工程,以及他们的目标、特殊性。
第二章 个人技术和流程
一个软件判定他是否优质,最主要的就是看它能否通过严格的测试,并且在测试过程中是否经过一系列的优化。
在第二章中,作者介绍了软件测试的概念与分类(单元测试、回归测试、用户测试),在此之前编写过得程序测试都是简单的结果是否符合预期,没有接触过单元测试之类,在明白了其原理后,让我对测试有了更深的理解。
除此之外,作者用两种方式讲解分析和性能改进,并且强调:虽然优化很重要,但是如果我们不经分析就盲目的进行优化,也许只会事倍功半。还介绍了软件的个人开发流程,有CMMI和PSP,以及后者对于软件开发流程的分配。
第三章 软件工程师的成长
软件工程师成长的过程中也需要很多衡量指标,一个成熟的软件工程师应该具有在稳定的时间内交付任务的能力。此外作者用魔方为喻,形象的描述如何评判自身的能力,真正的精是明白其中的原理,而不能只局限于底层的使用,就像魔方你可能会公式把他从打乱的情况还原,但是你能把他再打乱回原来的情况吗,这就体现出专与精的差别了。
PSP表格
| PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 50 |
| Estimate | 估计这个任务需要多少时间 | 60*12 | 60*15 |
| Development | 开发 | 60*8 | 60*12 |
| Analysis | 需求分析(包括学习新技术) | 60*3 | 60*5 |
| Design Spec | 生成设计文档 | 60 | 60 |
| Design Review | 设计复审 | 20 | 20 |
| Coding Standard | 代码规范(为目前开发制定合适的规范) | 30 | 60 |
| Design | 具体设计 | 60 | 80 |
| Coding | 具体编码 | 60*6 | 60*10 |
| Code Review | 代码复审 | 30 | 60 |
| Test | 测试(自我测试,修改代码,提交修改) | 60 | 60*2 |
| Reporting | 报告 | 60 | 70 |
| Test Report | 测试报告 | 60 | 30 |
| Size Measurement | 计算工作量 | - | - |
| Postmortem & Process Improvement Plan | 事后总结,并提出过程改进计划 | 60 | 60 |
| 合计 | 2210 | 3130 |
二.解题思路
-
需求分析
- 给定文件夹路径,其中日志文件记录着各个省份确诊,感染,治愈,死亡数量变化情况。
- 程序能读取命令并根据命令读取相关文件以及输出对应格式
- 程序能根据文件内容进行数据的加工计算
- 能够将计算结果按格式输出到指定文件中。
-
实现思路
阅读了这个题目以后,花了一段时间分析了下需求,并根据需求逐步构想实现思路,大致如下:
- 构建命令解析函数,因为命令是当做主函数的参数传入,不解析的话就不知道这个命令到底是嘛意思,更别谈后续根据命令读取文件和输出数据,解析过程中创建一个命令实体用来存放命令的选项啊,参数啥的。
- 构建完命令解析器,创建了命令实体,接下来当然也需要数据结构来存储各个省份的疫情数据,初始化为0。
- 做完以上就可以根据命令读取文件内容啦,以行读取根据正则匹配数据变化情况,并同时计算更新数据结构中的疫情数据。
- 最后再根据命令将数据按格式输出到指定文件中就OK了。

三.实现过程
程序中涉及的类:如下
- command:interface 充当一个接口
- SendCommandController:命令发送者类
- ReceiveCommand:命令接收者类
- List:list命令执行类,implement于command
- CommandAnalyze:命令解析类
- ListCommand:list命令类实体
- FileOperate:文件操作类
- Time:日期类,包含一个日期比较函数
- RegularExpression:正则工具类
在实现之前,上网了解了下什么是设计模式中的命令模式,大致就是把请求发送和请求接收分离开,看到网上示例就感觉用到这个层次结构一下就清楚了好多,以下为使用了命令模式的过程图

四.代码说明
关键的代码的话有
-
命令解析函数
命令解析中使用switch case语句选择命令类别进行判断,格式判断,构建命令实体实例以及命令发送控制器相关命令函数的执行。
public void commandAnalysis(SendCommandController sendCommandController)
{
switch(cmd[0])
{
case "list":
{
if(!RegularExpression.isListRight(wholeCmd))
{
System.out.println("list命令格式错误");
return;
}
ListCommand listCommand = new ListCommand(cmd);
if(listCommand.getOption()[0])
listCommand.dateParameter();
if(listCommand.getOption()[1])
listCommand.typeParameter();
if(listCommand.getOption()[2])
listCommand.provinceParameter();
listCommand.logParameter();
listCommand.outParameter();
sendCommandController.list(listCommand);
break;
}
default:
System.out.println("没找到 "+cmd[0]+" 命令");
}
}
- 以行为单位读取文件并修改疫情数据,运用switch语句和正则匹配决定进行何种数据修改
/*用于读取单个文件*/
public void readFile(String path)
{
int num;//存取数据修改
RegularExpression regularExpression = new RegularExpression();
File file=new File(path);//创建文件对象
String encoding="UTF-8";//设置读取文件的编码格式
if(file.isFile()&&file.exists()){//判断文件是否存在
try {
FileInputStream fisr=new FileInputStream(file);
InputStreamReader isr=new InputStreamReader(fisr,encoding);//封装文件输入流,并设置编码方式
BufferedReader br=new BufferedReader(isr);
String txt=null;
while((txt=br.readLine())!=null){//按行读取文件,每次读取一行
String[] splited = txt.split("\\s+");
String number;
int number1;
int number2;
switch(regularExpression.fileMatch(txt))
{
case 1:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[0][number1] = data[0][number1] + num;
ifChanged[number1] = true;
break;
case 2:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[1][number1] = data[1][number1] + num;
ifChanged[number1] = true;
break;
case 3:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
number2 = this.getProvinceId(splited[3]);
data[0][number1] = data[0][number1] - num;
data[0][number2] = data[0][number2] + num;
ifChanged[number1] = true;
ifChanged[number2] = true;
break;
case 4:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
number2 = this.getProvinceId(splited[3]);
data[1][number1] = data[1][number1] - num;
data[1][number2] = data[1][number2] + num;
ifChanged[number1] = true;
ifChanged[number2] = true;
break;
case 5:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[0][number1] = data[0][number1] - num;
data[3][number1] = data[3][number1] + num;
ifChanged[number1] = true;
break;
case 6:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[0][number1] = data[0][number1] - num;
data[2][number1] = data[2][number1] + num;
ifChanged[number1] = true;
break;
case 7:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[1][number1] = data[1][number1] - num;
data[0][number1] = data[0][number1] + num;
ifChanged[number1] = true;
break;
case 8:
number = regularExpression.getSubUtilSimple(splited[splited.length-1]);
num = Integer.parseInt(number);
number1 = this.getProvinceId(splited[0]);
data[1][number1] = data[1][number1] - num;
ifChanged[number1] = true;
break;
default:
break;
}
}
fisr.close();
isr.close();
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
}
五.单元测试截图
第一次接触单元测试,网上找教程摸爬滚打的写完(现在还是不太熟悉),截取了部分命令分析截图,使用的是eclpise的JUnit。

六.单元覆盖率优化
覆盖率有点低,因为写的类有点多,可能一些地方没有测试到
注:测试是用测试项目做的,其中代码和作业代码相同


代码优化暂时没有大进展。
七.代码规范链接
八.心路历程与收获
这个作业对于我来说感觉还是有一些难度,可能是我的基础功不太扎实,在编写过程中思绪没有那么清晰,这点以后还多加练习改善。在本次作业中也熟悉练习了很多已经遗忘的java知识,以及很多新知识,收获感觉还是蛮大的吧,虽然打代码的时候感觉累了点,打完了就觉得有点小成就感哈哈哈。还是要继续加油,革命成功尚远,还需多加努力。
九.Git仓库
| 仓库1 | 基于Linux环境下搭建VPN |
|---|---|
| 仓库2 | 基于Linux的nginx相关shell脚本项目 |
| 仓库3 | 基于Linux的MySQL相关操作脚本 |
| 仓库4 | 基于Linux的php开发环境即php服务搭建脚本 |
| 仓库5 | 基于Linux安装memcache服务,并将memcache库配置到php |
浙公网安备 33010602011771号