软工实践寒假作业(2/2)
| 这个作业属于哪个课程 | 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 | 计划 | 30 | 30 | 
| Estimate | 估计这个任务需要多少时间 | 30 | 30 | 
| Development | 开发 | 1360 | 1140 | 
| Analysis | 需求分析 (包括学习新技术) | 30 | 40 | 
| Design Spec | 生成设计文档 | 20 | 20 | 
| Design Review | 设计复审 | 30 | 20 | 
| Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 20 | 
| Design | 具体设计 | 60 | 40 | 
| Coding | 具体编码 | 1200 | 1000 | 
| Code Review | 代码复审 | 20 | 30 | 
| Test | 测试(自我测试,修改代码,提交修改) | 60 | 180 | 
| Reporting | 报告 | 90 | 90 | 
| Test Repor | 测试报告 | 20 | 15 | 
| Size Measurement | 计算工作量 | 10 | 15 | 
| Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 60 | 
| 合计 | 1480 | 1260 | 
解题思路
读完题目,我最先考虑的是使用什么数据结构,我创建了一个结构体用于保存省份的疫情数据,然后使用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<string> type, vector<string> province);//通过解析的参数值调用相应操作
文件操作函数
bool isLogFile(string fileName);//通过文件名判断是否是日期文件
void ReadAllLog(string path, string date);//读取文件目录
void ReadLog(string filePath);//读取并解析文件
void OutLog(string filePath, vector<string> type, vector<string> province);//输出文件
关键函数流程图
ProcessOption函数:用于解析参数值

ReadLog函数:用于读取和处理文件

代码说明
主要数据结构:
map<string,CProvince> 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<string> type, vector<string> province)
{
    ofstream ofLog(filePath.c_str(),ios::out);//创建并写入新的日志文件
    if(!ofLog)
    {
        cout<<filePath<<" open failure.\n";
        exit(0);
    }
    for(int i=0; i<35; i++)//按照省份列表的顺序输出每个省份的数据信息
    {
        //指定打印参数中出现过的省份,若参数为空,输出日志文件中出现过的省份
        if((mapProvince[PROVINCENAME[i]].isPrint) || (!province.size() && mapProvince[PROVINCENAME[i]].isInLog))
        {
            ofLog<<PROVINCENAME[i]<<" ";
            for(int j=0; j<type.size(); j++)
            {
                switch(mapDataValue[type[j]])
                {
                    case ipValue:
                        ofLog<<"感染患者"<<mapProvince[PROVINCENAME[i]].ip<<"人 ";
                        break;
                    case spValue:
                        ofLog<<"疑似患者"<<mapProvince[PROVINCENAME[i]].sp<<"人 ";
                        break;
                    case cureValue:
                        ofLog<<"治愈"<<mapProvince[PROVINCENAME[i]].cure<<"人 ";
                        break;
                    case deadValue:
                        ofLog<<"死亡"<<mapProvince[PROVINCENAME[i]].dead<<"人 ";
                        break;
                    default:
                        break;
                }
            }
            ofLog<<"\n";
        }
    }
    ofLog<<"// 该文档并非真实数据,仅供测试使用";
    ofLog.close();
}
思路:遍历所有的省份,在-province参数中出现的参数值isPrint将被标记为true,在日志文件中操作过的参数值isInLog将被标记为true;当-province有参数值,则输出isPrint为true的省份,否则,输出isInLog为true的省份。而在ip、sp、cure、dead的输出顺序中,我将这几个参数值存入了一个vector
单元测试
使用Visual Studio 2017的Microsoft CppUnitTest框架编写的C++测试单元。
测试有返回值的函数:

测试无返回值的函数:

使用相同的方法,编写了十组UnitTest

完整运行结果:



(PS:下方命令注释由于存放路径差异与样例有一定出入,但是是符合逻辑的。)
单元测试覆盖率优化和性能测试
单元测试覆盖率优化

经过覆盖率检验,发现部分方法的覆盖率较低,可能是因为没有覆盖到一些错误处理的分支,于是改善了一下单元测试。

覆盖率得以提高,剩下一部分未覆盖的是异常处理情况。Visual Studio的测试似乎无法检测exit()。
性能测试
CPU使用率:

代码规范
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库。

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号