[BUAA软工]结对作业
本次作业所属课程: 2019BUAA软件工程
本次作业要求: 结对项目
我在本课程的目标: 熟悉结对合作,为团队合作打下基础
本次作业的帮助:理解一个c++ 项目的开发历程
【1】项目github
Pre
【2】pre -1. PSP 表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 60 | 80 |
· Estimate | · 估计这个任务需要多少时间 | 14天 | 14天 |
Development | 开发 | 7天 | 9天 |
· Analysis | · 需求分析 (包括学习新技术) | 0.5天 | 0.7天 |
· Design Spec | · 生成设计文档 | 120 | 150 |
· Design Review | · 设计复审 (和同事审核设计文档) | 60 | 60 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 50 |
· Design | · 具体设计 | 1天 | 1天 |
· Coding | · 具体编码 | 4天 | 5天 |
· Code Review | · 代码复审 | 1天 | 1.5天 |
· Test | · 测试(自我测试,修改代码,提交修改) | 1天 | 1天 |
Reporting | 报告 | 1天 | 1天 |
· Test Report | · 测试报告 | 2小时 | 4小时 |
· Size Measurement | · 计算工作量 | 1小时 | 1小时 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 0.5天 | 0.5天 |
合计 | 10天 | 13天 |
【3】pre-2 学习接口设计
看教科书和其它资料中关于Information Hiding, Interface Design, Loose Coupling的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的
(1)Information Hiding:[wiki]In computer science, information hiding is the principle of segregation of the design decisions in a computer program that are most likely to change, thus protecting other parts of the program from extensive modification if the design decision is changed. The protection involves providing a stable interface which protects the remainder of the program from the implementation (the details that are most likely to change).
我们小组使用类对于数据进行封装,保证稳定的接口,来保护程序的其他部分不受影响。
(2)Interface Design
接口基本与作业要求一致,尽量将不同的实体封装,尽量少使用全局变量,全局函数。
(3)Loose Coupling
松耦合,与别的小组互换core、gui和test, 对方封装得足够好,调用起来就非常方便,我们自己的模块封装性不好,技术上还需要提升。
具体设计
1. Core模块设计
【4】计算模块接口的设计与实现过程。
设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处
设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处
有三个类,word类负责保存单词的基本属性,wordlist类则负责保存单词链,maxtrix的主要作用是用来计算相应的最长单词链。计算最长单词链的算法是用到了拓扑排序,首先将所有单词中的没有办法和其它单词练成链的单词去除。然后构建邻接矩阵。之后通过拓扑排序以及动态规划找出最长的单词链和最多字母的单词链。
第一个版本存在着各式各样的问题:
严重性 代码 说明 项目 文件 行 禁止显示状态
警告 C26495 Variable 'Maxtrix::row' is uninitialized. Always initialize a member variable (type.6). Wordlist c:\users\a\desktop\projects\worldlist(4)\worldlist\worldlist\maxtrix.cpp 6 活动的
......
......
......
警告 C4101 “length”: 未引用的局部变量 Wordlist c:\users\a\desktop\projects\worldlist(4)\worldlist\worldlist\maxtrix.cpp 166
警告 C4101 “maxlength”: 未引用的局部变量 Wordlist c:\users\a\desktop\projects\worldlist(4)\worldlist\worldlist\maxtrix.cpp 242
警告 C4101 “fileWord”: 未引用的局部变量 Wordlist c:\users\a\desktop\projects\worldlist(4)\worldlist\worldlist\main.cpp 28
后续的版本加以改进了。
【5】画UML
阅读有关UML的内容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。画出UML图显示计算模块部分各个实体之间的关系(画一个图即可)。
自动生成的UML
类其实比较简单,Matrix调用Wordlist, Wordlist调用Word.
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
描述这些做法的优缺点, 说明你是如何把它们融入结对作业中的
契约编程
严格规定前置条件,后置条件,不变项,可以很好的规范程序接口,程序的正确性得到了更大的保证, 但是这一做法比较费时费力,不那么适用于敏捷开发。但是我们可以对于核心的,非常重要的模块进行接口的严格规定,约束程序行为,让开发兼顾开发效率和正确性。
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 kits\10\include\10.0.17763.0\ucrt\string.h:line 124
at Wordlist::add() in c:\users\xxx\desktop\worldlist(4)\worldlist(4)\worldlist\worldlist\wordlist.cpp:line 11
at Maxtrix::findMaxLengthChain() in c:\users\xxx\desktop\worldlist(4)\worldlist(4)\worldlist\worldlist\maxtrix.cpp:line 168
at UnitTest1::UnitTest1::testMaxtrix() in c:\users\xxx\desktop\worldlist(4)\worldlist(4)\worldlist\unittest1\unittest1.cpp:line 59
结果 消息: Invalid parameter detected in function common_tcscpy_s, minkernel\crts\ucrt\inc\corecrt_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】计算模块部分异常处理说明。
在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。
异常:
- 指令错误
- 不是-r的指令也出现了环
- 文件不存在
- 得到的结果中只有一个单词,无法构成环
目标:
1. 输出Instruction error!
2. 输出There are one or more circles
3. 输出file is not existed!
4. 输出Only one word in the chain
单元测试样例:
- Wordlist.exe -h p -t z F:\VS2017\Worldlist\Debug\test.txt
- Wordlist.exe -h p -t z -w F:\VS2017\Worldlist\Debug\test.txt,在test.txt里头出现了带环的链
- Wordlist.exe -h e -t t -c F:\VS2017\Worldlist\Debug\tet.txt
- Wordlist.exe -h e -t t -c F:\VS2017\Worldlist\Debug\test.txt,此时test里头除了唯一一个以e开头以t结尾的单词之外没有另外的单词
5.GUI设计
【10】界面模块的详细设计过程。
在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。
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 说明结对编程的优点和缺点。结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。
优点:
- 相互review,相互学习
- 可以提升程序的质量
- 合理组队可以让团队发挥1+1>2的作用
缺点:
- 有的人就是喜欢一个人完整构建整个工程,工程代码的一致性、完整性也很好
- 两人意见分歧的时候,因为只有两个人,更加容易形成对立,分工不合理也可能造成矛盾
- 只有两个人,如果两个人都比较菜,技能点又都相似, 那么结对编程也很难发挥优势。
我的结对的同学,首先一点就是态度非常好,非常愿意付出时间和汗水去把结对这个事情做好,第二是很容易沟通,很和善的一个人。第三是解决问题的能力也比较强,能够独立去思考和解决编程中遇到的bug。如果非要说有缺点的话,那么可能代码的风格还需要注意一点,然后就是一些编程的细节需要注意一下,那对于我来说,就已经是很好的队友了。
我的话学东西比较快,也相对容易沟通,指导一点工程上需要注意的东西。但是我比较倾向于做相对有趣的东西,不是很喜欢私钻算法,偶尔也容易急躁。
【14】在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。
答: 在文章首部