[BUAA软工]结对作业
本次作业所属课程: 2019BUAA软件工程
本次作业要求: 结对项目
我在本课程的目标: 熟悉结对合作,为团队合作打下基础
本次作业的帮助:理解一个c++ 项目的开发历程
【1】[项目github]
https://github.com/butub1/WordList
Pre
【2】pre -1. PSP 表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 50 | 80 |
· Estimate | · 估计这个任务需要多少时间 | 14天 | 14天 |
Development | 开发 | 6天 | 9天 |
· Analysis | · 需求分析 (包括学习新技术) | 1天 | 0.7天 |
· Design Spec | · 生成设计文档 | 180 | 150 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 50 |
· Design | · 具体设计 | 1天 | 1天 |
· Coding | · 具体编码 | 5天 | 5天 |
· Code Review | · 代码复审 | 1天 | 1.5天 |
· Test | · 测试(自我测试,修改代码,提交修改) | 1天 | 1天 |
Reporting | 报告 | 1天 | 1天 |
· Test Report | · 测试报告 | 5小时 | 4小时 |
· Size Measurement | · 计算工作量 | 1小时 | 1小时 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 0.5天 | 0.5天 |
合计 | 14天 | 13天 |
【3】pre-2 学习接口设计
看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的
(1)Information Hiding:
采用了Interface这个类来对数据进行封装。
(2)Interface Design
接口的要求与作业一只
(3)Loose Coupling
松耦合,与别的小组互换core、gui和test, 对方封装得足够好,调用起来就非常方便,我们自己的模块封装性不好,技术上还需要提升。
具体设计
1. Core模块设计
【4】计算模块接口的设计与实现过程。
设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处
有三个类,word类负责保存单词的基本属性,wordlist类则负责保存单词链,maxtrix的主要作用是用来计算相应的最长单词链。计算最长单词链的算法是用到了拓扑排序,首先将所有单词中的没有办法和其它单词练成链的单词去除。然后构建邻接矩阵。之后通过拓扑排序以及动态规划找出最长的单词链和最多字母的单词链。
【5】画UML
2.首个版本性能测试
【6】计算模块接口部分的性能改进。
记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展示你程序中消耗最大的函数
一开始我采用的算法是,再把所有的单词进行拓扑排序之后,找出所有的链,然后对所有的链进行统计,找出其中最多字母的链。但是当单词数多起来比如达到了9000个单词之后速度特别慢。很长时间都跑不出来结果。
后来我修改了算法,再计算字母最多链的时候,引入了动态规划算法,算出了起始点到终点的最长距离,并且记录了其前驱节点。改进完毕之后速度快了很多,可以正常跑完9000个无环的测试样例。
【7】看Design by Contract, Code Contract的内容:
http://en.wikipedia.org/wiki/Design_by_contract> http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的
契约式设计的优点在于能够明确每个类的功能,以及类之间的联系。每个类负责固定的几个功能,然后将其结果供之后的类使用。这样方便了代码的测试,重构,因为每段代码的“合同”都已经完整记录。
在我的设计当中,比如我的一个函数为topuSort,它能够将单词链的图所有的节点进行拓扑排序,然后将这个排序之后的列表返回出来。这个函数期望得到一个图,能够保证将其进行拓扑排序,然后将其结果维持并且返回出来。
3. 测试用例及结果
【8】计算模块部分单元测试展示。
展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到90%以上,否则单元测试部分视作无效。
用例:
TEST_METHOD(testWord)
{ Word* w = new Word();
w->setWordName("abc");
w->setIndegree(0);
w->setMaxLength(strlen("abc"));
w->setSearched(true);
w->setTopuLevel(3);
Assert::AreEqual(w->getFirstAlp(), 'a');
Assert::AreEqual(w->getLastAlp(), 'c');
Assert::AreEqual(w->getWordLength(), 3);
free(w);
结果:正确,构造这条数据验证Word类的正常功能,这是整个程序中最小最基本的数据抽象封装。
用例:
TEST_METHOD(testWordlist)
{ Wordlist *wl = new Wordlist();
char* set[] = { "hello","on","next","take","err" };
for (int i = 0; i < 5; i++) {
wl->add(set[i]);
}
Assert::AreEqual(wl->getAlpNum(),18);
Assert::AreEqual(wl->getFirstLetter(), 'h');
Assert::AreEqual(wl->getLastLetter(), 'r');
free(wl);
}
结果:正确, 测试wordlist类的正常功能,这个类用来表示计算得到的最长链
用例:
TEST_METHOD(testMaxtrix) {
Maxtrix m //= new Maxtrix();
char* set[] = { "hello","on","next","take","aaa" ,"abbc","dd" };
char* result[] = { "hello","on","next","take" };
for (int i = 0; i < 7; i++) {
m->setWord(set[i], i);
}
m.deleteOutlier();
m.initialMatrix();
m.createMatrix();
m.topuSort();
m.findInMaxtrix();
m.findMaxLengthChain();
for (int i = 0; i < m->wordChain.wordNum; i++) {
Assert::AreEqual(m->wordChain.wordList[i], result[i]);
}
}
结果1:错误,需要注意的问题是,在main.cpp中声明了一个全局的Maxtrix对象matrix,但是Maxtrix.h中设定max_word_num = 10005;
而且有一个属性int maxtrix[max_word_num][max_word_num];
作为邻接矩阵, 这么大的二维数组放在全局空间是没有问题的, 但是在测试的时候,声明一个maxtrix是会爆栈的,Exception Code: C00000FD
, 还是用new 或者malloc来管理内存吧。
在默认情况下,栈只能得到1M大小的内存,全局静态储存可以得到2G,而在32位和64位下的堆则可以得到2G和无限内存(一般不会用到16T)。
结果2:
结果 StackTrace:
at strcpy_s<1000>() in c:program files (x86)windows kits10include10.0.17763.0ucrtstring.h:line 124
at Wordlist::add() in c:usersxxxdesktopworldlist(4)worldlist(4)worldlistworldlistwordlist.cpp:line 11
at Maxtrix::findMaxLengthChain() in c:usersxxxdesktopworldlist(4)worldlist(4)worldlistworldlistmaxtrix.cpp:line 168
at UnitTest1::UnitTest1::testMaxtrix() in c:usersxxxdesktopworldlist(4)worldlist(4)worldlistunittest1unittest1.cpp:line 59
结果 消息: Invalid parameter detected in function common_tcscpy_s, minkernelcrtsucrtinccorecrt_internal_string_templates.h line 81. Expression: (L"Buffer is too small" && 0)
用例:
TEST_METHOD(testMaxtrix) {
Maxtrix* m = new Maxtrix();
char* set[] = { "Algebra","Apple","Zoo","Elephant","Under","Fox",
"Dog","Moon","Leaf","Trick","Pseudopseudohypoparathyroidism" };
char* result[] = { "Algebra","Apple","Elephant" };
Assert::AreEqual(1, 1);
for (int i = 0; i < 7; i++) {
m->setWord(set[i], i);
}
m->deleteOutlier();
m->initialMatrix();
m->createMatrix();
m->topuSort();
m->findInMaxtrix();
m->findDesignatedChain('a', 't', 2);
for (int i = 0; i < m->wordChain.wordNum; i++) {
Assert::AreEqual(m->wordChain.wordList[i], result[i]);
}
结果:正确,测试固定头尾的功能函数的正确性。
覆盖率截图
覆盖率截图
4.【9】异常处理
【9】计算模块部分异常处理说明。
在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。
异常:
1,指令错误
2,不是-r的指令也出现了环
3,文件不存在
4,得到的结果中只有一个单词,无法构成环
目标:
1,输出Instruction error!
2,输出There are one or more circles
3,输出file is not existed!
4,输出Only one word in the chain
单元测试样例:
1,Wordlist.exe -h p -t z F:VS2017WorldlistDebug est.txt
2,Wordlist.exe -h p -t z -w F:VS2017WorldlistDebug est.txt,在test.txt里头出现了带环的链
3,Wordlist.exe -h e -t t -c F:VS2017WorldlistDebug et.txt
4,Wordlist.exe -h e -t t -c F:VS2017WorldlistDebug est.txt
此时test里头除了唯一一个以e开头以t结尾的单词之外没有另外的单词
在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。
gui使用QT实现,用qt creater这个ide来设计前端的界面,非常方便,可以通过拖拉放置来构建组件,而代码逻辑则可以通过绑定槽来将clicked这种动作链接到这个组件对应的一个函数中去,在函数中写组件需要的功能即可。
界面:
【11】界面模块与计算模块的对接。
详细地描述UI模块的设计与两个模块的对接,并在博客中截图实现的功能。
这一部分没能成功对接上,主要是生成dll后,在qt creater中导入dll失败了很多次。最后反倒是尝试结对解耦合时,改使用visual studio 2017 加上qt的插件,让界面模块可以和计算模块链接起来。
【12】描述结对的过程,
提供非摆拍的两人在讨论的结对照片。
我觉得和我的伙伴一起合作还算很愉快,大家都愿意为了完成项目而付出热情和汗水,很棒的一次体验。
6.松耦合
在博客中指明合作小组两位同学的学号,分析两组不同的模块合并之后出现的问题,为何会出现这样的问题,以及是如何根据反馈改进自己模块的。
合作小组:牛雅哲16131059
本组GUI+对方的核心模块Core
问题:核心Core的接口不同,合并需要修改一些逻辑,Core中自定义了参数类型和错误类型,接口为se_errcode Calculate(const string& input_text, string& output_text, LongestWordChainType& longest_type, const char& head, const char& tail, bool enable_circle);
按照这个接口写了一个版本,后来合作小组改成作业要求的标准接口了int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
和int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
处理:修改GUI调用接口的逻辑,匹配核心Core的接口
结果:能够正常运行
7.【13】
【13】看教科书和其它参考书,网站中关于结对编程的章节,
例如:
http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
说明结对编程的优点和缺点。
结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
结对编程的好处在于,两个人在一起开发,测试,两人合作写出的代码质量取决于水平高的那个人。结对时处于不断复审的过程,因此能够节省很多修改测试的时间。缺点在于两个人的沟通和磨合需要较长的时间,如果不能够好好的沟通和反馈,结对编程反而会影响效率。在结对中,我的优点在于善于沟通,思考全面,编码风格较好,缺点在于算法能力较差,需要花费大量的时间在研究实现的算法上。我的同伴的优点在于编码能力强,测试时思考的很全面,善于学习新的东西,缺点在于沟通和反馈,有时候我不太能理解它所说的。总的来说我们都非常愿意为共同的项目付出热情和汗水,这是非常难得的。
【14】在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。
实际时间见【2】。