这个作业属于哪个课程 | 2020春|S班(福州大学) |
---|---|
这个作业要求在哪里 | 寒假作业(2/2) |
这个作业的目标 | 1.学习使用github 2.制定代码规范 3.开发一个疫情统计程序 4.给出此次作业的PSP表格 5.进行单元测试和覆盖率测试 6.阅读《构建之法》 7.本次的心路历程和收获 |
作业正文 | 1.Github仓库地址 2.PSP表格 3.解题思路 4.设计实现过程 5.代码说明 6.单元测试 7.单元覆盖率优化和性能测试 8.代码规范 9.心路历程与收获 10.学习路线相关的5个仓库 |
其他参考文献 |
PS:C++实现
Github仓库地址
Github仓库地址:https://github.com/Cazenove/InfectStatistic-main
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 40 | 20 |
Estimate | 估计这个任务需要多少时间 | 30 | 20 |
Development | 开发 | 600 | 480 |
Analysis | 需求分析 (包括学习新技术) | 30 | 40 |
Design Spec | 生成设计文档 | 20 | 20 |
Design Review | 设计复审 | 30 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
Design | 具体设计 | 60 | 40 |
Coding | 具体编码 | 300 | 240 |
Code Review | 代码复审 | 20 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 180 |
Reporting | 报告 | 120 | 100 |
Test Repor | 测试报告 | 15 | 20 |
Size Measurement | 计算工作量 | 10 | 15 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 1375 | 1265 |
解题思路
读完题目,我最先考虑的是使用什么数据结构,我创建了一个结构体用于保存省份的疫情数据,然后使用STL的map来建立结构体与省份名称的映射关系。这样,在接下来的操作中就可以直接通过省份名称来访问信息数据。设计完数据结构,根据作业需求中的程序使用方法来看,程序运行的流程主要有五个步骤:1.解析参数 -> 2.读取日志文件 -> 3.统计信息 -> 4.输出文件
虽然可以着手开始写代码,但是在没有事前准备的情况下直接开始写,那就与以往无异了,于是我继续阅读了作业中的其他要求。
参考《腾讯c++代码规范》编写了代码规范,并初步设计了程序模块、类图、数据结构、算法流程等部分。
设计实现过程
模块结构图与总流程图
代码组织
省份类
/***********************
Description:存放某省份的数据以及对数据的操作
***********************/
class CProvince
{
public:
unsigned int ip;//确诊患者数量
unsigned int sp;//疑似患者数量
unsigned int cure;//治愈患者数量
unsigned int dead;//死亡患者数量
bool isInLog;//是否在日志文件中出现过
bool isPrint;//该省份是否要打印
CProvince();//初始化
void AddIP(int num);//感染患者增加
void AddSP(int num);//疑似患者增加
void MoveIP(string strProvince,int num);//感染患者流入其他省
void MoveSP(string strProvince,int num);//疑似患者流入其他省
void IPtoDead(int num);//感染患者死亡
void IPtoCure(int num);//感染患者治愈
void SPtoIP(int num);//疑似患者确诊为感染患者
void SubSP(int num);//排除疑似患者
};
命令参数处理函数
void ProcessOption(int argc,char *argv[]);//解析参数值
void list(string logPath, string outPath, string date, vector type, vector province);//通过解析的参数值调用相应操作
文件操作函数
bool isLogFile(string fileName);//通过文件名判断是否是日期文件
void ReadAllLog(string path, string date);//读取文件目录
void ReadLog(string filePath);//读取并解析文件
void OutLog(string filePath, vector type, vector province);//输出文件
关键函数流程图
ProcessOption函数:用于解析参数值
ReadLog函数:用于读取和处理文件
代码说明
主要数据结构:
map mapProvince;
思路:通过map建立string与CProvince(上文有给出类结构)之间的映射关系,后续可直接通过省份名称来访问类。
初始化函数:
static string PROVINCENAME[35] =
{
"全国","安徽","澳门","北京","重庆","福建","甘肃",
"广东","广西","贵州","海南","河北","河南","黑龙江",
"湖北","湖南","吉林","江苏","江西","辽宁","内蒙古",
"宁夏","青海","山东","山西","陕西","上海","四川",
"台湾","天津","西藏","香港","新疆","云南","浙江"
};
void init()//初始化
{
mapListValue["-log"] = logValue;
mapListValue["-out"] = outValue;;
mapListValue["-date"] = dateValue;
mapListValue["-type"] = typeValue;
mapListValue["-province"] = provinceValue;
mapDataValue["ip"] = ipValue;
mapDataValue["sp"] = spValue;
mapDataValue["cure"] = cureValue;
mapDataValue["dead"] = deadValue;
mapProvince.clear();//清空map
string strProvince;
for(int i=0; i<35; i++)
{
CProvince cprovince;
mapProvince[PROVINCENAME[i]] = cprovince;//建立省份名称与信息存储结构的映射关系
}
}
思路:初始化其实是本程序非常重要的一个环节,我事先用string数组将省份的顺序存入了程序中,在初始化的过程中,省份名称以及省份类的映射关系已经全部建立好了,后续的文件输出操作与这一步息息相关。由于C++不支持string类型的switch,前面的mapListValue和mapDataValue用于建立string和枚举类型的对应关系,用于将后面的if else结构转变为switch结构。
文件输出函数:
void OutLog(string filePath, vector type, vector province)
{
ofstream ofLog(filePath.c_str(),ios::out);//创建并写入新的日志文件
if(!ofLog)
{
cout<
思路:遍历所有的省份,在-province参数中出现的参数值isPrint将被标记为true,在日志文件中操作过的参数值isInLog将被标记为true;当-province有参数值,则输出isPrint为true的省份,否则,输出isInLog为true的省份。而在ip、sp、cure、dead的输出顺序中,我将这几个参数值存入了一个vector 中,当没有参数值时,vector 存入默认顺序的参数,当有参数值时,则存放给定的顺序。在输出时,根据vector 的参数顺序来进行文件输出。
单元测试
使用Visual Studio 2017的Microsoft CppUnitTest框架编写的C++测试单元。
测试有返回值的函数:
测试无返回值的函数:
完整运行结果:
(PS:下方命令注释由于存放路径差异与样例有一定出入,但是是符合逻辑的。)
单元测试覆盖率优化和性能测试
单元测试覆盖率优化
经过覆盖率检验,发现部分方法的覆盖率较低,可能是因为没有覆盖到一些错误处理的分支,于是改善了一下单元测试。
覆盖率得以提高,剩下一部分未覆盖的是异常处理情况。Visual Studio的测试似乎无法检测exit()。
性能测试
代码规范
https://github.com/Cazenove/InfectStatistic-main/blob/master/221701210/codestyle.md
心路历程与收获
在阅读《构建之法》和完成本次作业的过程中,我获益良多。我认真思考了一个问题:怎样才算一个优秀的程序员?因为我个人是高中NOIP开始接触编程,所以我个人的理解是能写出巧妙的代码,但是在学习《构建之法》后,我发现这个观点是有问题的。编程能力诚然是程序员最为重要的能力之一,但我们的课程叫做“软件工程”,“软件工程是把系统的、有序的、可量化的方法应用到软件的开发、运营和维护上
的过程。”,工程能力也是程序员的重要能力,在编程比赛中获奖的选手,可能写的代码只有自己看得懂,如果在项目中以这种方式写代码,那将很难与他人进行协作,一个人可能可以完成中小型项目,但大型项目一定是需要协作的,更不用说项目周期长了,自己都不知道之前的自己写了什么。软件工程之所以叫做工程,正是因为它与传统的土木工程等一样,有了系统、有序、可量化的方法。
这一次的作业算是让我们体验了一套完整的项目开发周期,以往做大作业、比赛项目的时候,由于开发规模较小(还有懒),往往没有先进行设计和完成后续的单元测试、覆盖率测试等,这导致了软件后期优化难以进行。在这次作业里,起初我以为代码是这次作业里面最难的部分,但是后来发现,单元测试才是最麻烦的。我在覆盖率测试的时候遇到了问题,Visual Studio默认的测试项目覆盖率一直有问题,用了google test也将一些与项目无关的东西计入覆盖率导致覆盖率奇低,由于大部分同学都是使用java,与我的测试工具不太相同,我尝试了许多方法换了许多工具都无法解决,经过一天多的研究,我才最终解决,我把解决方法写在了博客里:Visual Studio C++覆盖率测试异常的解决方法
我想,这也是为什么会要求我们写博客的原因。我在解决问题的时候,多么希望我能看到这样一篇博客。俗话说好记性不如烂笔头,未来的我如果忘记这个问题怎么解决,也可以查看以前写的记录。
学习路线相关的5个仓库
ActionRPGGame
https://github.com/iniside/ActionRPGGame
简介:一个动作RPG的游戏Demo,主要是为了演示作者用UE4做的一个RPG游戏框架。
MySQLConnectorUE4Plugin
https://github.com/KhArtNJava/MySQLConnectorUE4Plugin
简介:一个UE4插件,可以使UE4连接到MySQL数据库,支持C++和蓝图方式。在虚幻引擎4.13版本开发,支持32位和64位
UE4_DarkSoulsCamera
https://github.com/donanroherty/UE4_DarkSoulsCamera
简介:在虚幻4引擎中实现的黑魂风格相机,它支持锁定目标,在可用目标之间滚动,摄像机相对角色移动以及可选的软锁定系统,该系统可自动管理锁定范围内最接近的目标。项目是用C++构建的,带有一些用于动画树的蓝图逻辑。
GameSavePlugin
https://github.com/marshal-it/GameSavePlugin
简介:虚幻4的游戏保存插件,能够保存数据对象类,目前所有操作接口都已经默认在UGameplayStatics。
sphinx-ue4
https://github.com/shanecolb/sphinx-ue4
简介:Sphinx-UE4是用于虚幻引擎4的语音识别插件,使用了Pocketsphinx库。