[2019BUAA软件工程]结对作业
Tips | Link |
---|---|
作业链接 | [2019BUAA软件工程]结对作业 |
GitHub地址 | WordChain |
PSP表格
psp2.1 | 预估耗时(分钟) | 实际耗时(分钟) | |
---|---|---|---|
Planning | 计划 | 60 | 40 |
. Estimate | · 估计这个任务需要多少时间 | 900 | 1200 |
Development | 开发 | 700 | 900 |
. Analysis | · 需求分析 (包括学习新技术) | 60 | 60 |
. Design Spec | · 生成设计文档 | 100 | 120 |
.Design Review | · 设计复审 (和同事审核设计文档) | 40 | 20 |
.Coding Standard | · 代码规范 (为目前的开发制定合适的规范 | 40 | 30 |
.Design | · 具体设计 | 60 | 50 |
.Coding | · 具体编码 | 200 | 320 |
.Code Review | · 代码复审 | 100 | 200 |
.Test | · 测试(自我测试,修改代码,提交修改) | 100 | 100 |
Reporting | 报告 | 200 | 300 |
.Test Report | · 测试报告 | 60 | 100 |
.Size Measurement | · 计算工作量 | 100 | 120 |
.Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 | 80 |
合计 | 960 | 1240 |
模块化设计思想
1、信息隐藏(Information Hiding)
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).
以上部分摘自维基百科中对information hidding定义。信息隐藏的主要实现方式即使对我们自己程序的封装,比如将功能稳定的函数,抽象出来封装为私有,以实现在做出修改时,尽可能的保证程序其余部分的正确性,减少维护难度。另一方面,将程序中的变量都封装位私有,将这些信息隐藏起来,也可以较好的保证用户信息安全。
在我们的程序中,我们采用将很多功能抽象出子函数,只给用户暴漏了一个计算的入口函数,较好的实现了信息封装以及程序的可维护性,所有的变量也都采用私有变量的形式,增强了信息安全的可靠性。
2、接口设计(Interface Design)
User interface design (UI) or user interface engineering is the design of user interfaces for machines and software, such as computers, home appliances, mobile devices, and other electronic devices, with the focus on maximizing usability and the user experience.
以上部分摘自维基百科中对interface design 定义。接口设计的主要目的,是为了其他的用户或者应用提供一个应用或者用户可以直接使用的功能接口(即使别人完全不知道内部实现也可以轻易使用),原则就是封装的越完全越好,接口调用越简单越好。在我们的程序中,采用和作业指导中相同的接口设计,一方面实现了对程序功能较好的封装,一方面也极大的方便了我们与其他同学的耦合。
3、降低耦合(Loose Coupling)
In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services.[1] Loose coupling is the opposite of tight coupling.
以上部分摘自维基百科中对loose coupling定义. 程序中模块之间的耦合性主要是体现在模块之间的依赖关系,降低模块之间的耦合性,既可以最大程度上的实现模块中的功能分离,也可以更方便的对模块的功能之间进行测试。在我们的程序中方,我们将数据出来模块,与运算 模块完全独立 开,通过接口传递数据,极大的降低了模块之间的依关系,也较好的方便了对每个模块正确性的单独测试。
计算模块接口的设计与实现过程
对外接口设计:
接口:
bool gen_chain_word(vector<string> &words, vector<string> &output, char head, char tail, bool enable_loop);
bool gen_chain_char(vector<string> &words, vector<string> &output, char head, char tail, bool enable_loop);
参数解释:
vector<string> &words:传入单词集合。
vector<string> &output:输出单词链。
char head:单词链首字母限制。无限制输入0,有限制则输入相应英文字符。
char tail:单词链首字母限制。无限制输入0,有限制则输入相应英文字符。
bool enable_loop:单词环限制。允许存在单词换为true,否则为false。
类功能设计:
Core:内外层交互类
实现计算模块与外层(Console或GUI模块)交互逻辑。
接收外层的原始数据和相应参数。
对接收到的原始数据进行预处理:
从原始数据中提取出单词。
过滤重复单词。
将有效单词按首尾字母归类,存入单词库中。
更新单词库中单词链接矩阵。
按照指定模式组织内层计算逻辑。
返回内层计算结果。
Solve/DPSolve:核心计算类
实现对已有单词库搜索单词链的算法。
核心算法:优化BFS(有单词环)和动态规划(无单词环)。
辅助算法:拓扑排序(检查是否存在单词环)。
WordList:数据类
格式化存储单词。
将单词链接关系组织为邻接矩阵。
提供相应的查询函数。
类流程关系
Core获取原始数据与运行参数。
Core预处理原始数据,生成单词库(WordList)。
按照参数启动Solve/DPSolve相应计算函数。
完成单词链搜索,Core输出计算结果。
类关键函数设计及实现
Core:
关键函数:
gen_chain_word():按单词数量搜索单词链时的对外接口。
gen_chain_char():按单词长度搜索单词链时的对外接口。
WordList:
存储结构设计思路:
单词表组织: 将消除重复后的单词按照首尾字母进行归类。比如:apple归类为ae类单词。为每一种类的单词建立数组存储,每个数组内的同种单词按照长度降序排列。对存储每一种类单词的数组按照HASH表存储至一维数组中。
辅助结构组织: 根据已经建立的单词表,将字母视为节点,单词视为由首字母结点指向尾字母的有向边,则能够将单词表组织为有向图。使用位图存储邻接矩阵,使用HASH数组存储各结点的入度。
HASH函数:Index = 首字母相对'a'偏移 * 26 + 尾字母相对'a'偏移
成员变量:
m_list:单词表,按照首尾字母的种类对单词进行分组储存,存储采用HASH结构,不存在重复单词,同种类单词按照长度由长至短排列。
m_iListSize:单词表长度,存储不同首尾字母的单词数量。
m_iListGetPoint:单词检索索引,存储已检索的单词的索引。
m_iArryMatrix[]:单词链邻接矩阵。
m_iArrayNodeIn[]:结点入度数组。
关键函数:
parseString():
解析字符串,提取单词。
调用私有函数addWord添加单词。
getWordAt():
获取符合首尾条件的单词。
能够支持对相同单词的不重复搜索与重复搜索。
getWordSumAt():
获取符合首尾条件的单词总数量。
getWordRemainingAt():
获取符合首尾条件的单词剩余数量。
剩余数量指未被操作搜索过的单词。
undoGetWordAt():
撤销单词不重复搜索操作,即撤销对某首尾单词的前一次不重复搜索操作。
getNodeIn():
获取单词链图某一首字母结点的入度。
getNodeNext():
获取单词链图某一首字母结点的邻接矩阵项。
addWord():"private"
过滤重复单词,将有效单词添加至单词表相应分类。
保证添加后各分类内单词长度降序排列。
Solve:
实验思路
使用深度优先搜索加剪枝策略,解决了头尾限定问题,以及属于单词序列中存在环时得问题。
成员变量:
word
m_iSigned26 标记两个字母之间的路径是否以及走过
m_ihead[26] 标记每个字母是否以及用过
m_ans676 记录单词是否使用的变量
next_tag26 储存字母邻接链表
max_dfs[26] 记录每个单词距离终点的最大距离
final_dfs[26] 记录最终的每个字母节点距离其重点的距离
m_FinalLen 单词链最终长度
m_TemLen 搜索到的当前字符链的长度
max_num 单词链个数最大长度
temp_num 搜索到的单词链表当前个数
bool is_circle 判断是否有环
head 确定头部字母记录下的头部字母
char m_Mode 记录搜索模式的变量
char m_ModeHead 记录搜索的时指定的头部
char m_ModeTail 记录搜索的时指定的尾部
bool m_ModeRing 记录搜索模式是否有环
vector <string> m_FinalChain; 最终结果
vector <string> m_TempChain; 当前路径
关键方法
Solve1() 解决允许有环时的搜索问题
Solve2_he() 解决无环时的指定头尾的搜索问题
Dfs_solve1() 解决允许有环时的搜索问题的搜索函数
cmp() 允许有环的时候对当前最大长度的更新函数
printChain() 允许有环的时候的输出函数
Dfs_solvehe() 指定头尾问题的搜索问题的递归搜索函数
cmp_he() 解决头尾指定问题时候 对当前最大长度进行更新的函数
printhe() 解决头尾指定问题时候 的输出问题
DPSolve:
实现思路:
使用动态规划解决无单词环情况下除同时限定单词链首尾的问题。根据限制条件的不同采取正向或逆向动态规划。
动态规划:
正向递推式:F(c1)=max{F(c1)+len(c1ci)}或F(c1)=max{F(c1)+len(c1ci)}
逆向递推式:f(c1)=max{f(c1)+len(cic1)}或f(c1)=max{f(c1)+len(cic1)}
F(c)为由c开始的最长单词链长度。
f(c)为以c结束的最长单词链长度。
len(cicj)是以ci为首以cj为尾的最长单词长度。
单词环检测:
使用拓扑排序算法检测单词环,保证动态规划正确进行。
成员变量:
m_ptrWordList:单词表指针。
m_cMode:搜索模式,分为按数量和按程度。
m_cModeHead:头部限定。
m_cModeTail:尾部限定。
m_iArrayDp[]:动态规划结果。
m_iArrayNext[]:存储相应结点的最优后继结点。
m_iArrayBefore[]:存储相应结点的最优前缀结点。
m_iQueueTopo:拓扑排序结果。
m_strVecWordChain:存储计算得到的单词链。
关键方法:
DPSolve():构造函数,传入单词表和各种参数。
startDPSolve():启动函数。
topoSort():拓扑排序,检查单词表中是否有环。
getWordChain():导出计算后的结果。
DPStep(int indexH):正向动态规划子函数,私有函数。
DPStepRe(int indexH):逆向动态规划子函数,私有函数。
UML设计
计算模块接口部分的性能改进
第一阶段:深度搜索实现基本功能
笔者在最初进行程序设计时采取的是使用简洁的方法先实现需求的所有功能,再根据实际运行情况优化算法。根据这样的初衷产生了如下的流程设计。 首先,将所有单词按照首尾字母进行分类。以字母为结点,以单词为有向边(由单词首字母结点指向尾字母节点),将单词表构造成图结构。如此组织单词表可以很好地对需求中的问题进行简化和抽象:无单词环时,很容易证明每个结点间最多单向经过一次。比如'a'、'b'两结点最多仅能有一个有向边。于是便可以将问题转化为在构造的图中搜索最长路径。对于‘-w’功能则是将边权重设置为‘1’;对于‘-c’功能则是将边权重设置为该边对应类型单词中最长的单词长度;而‘-t’和‘-h’功能则是增加了首尾节点的限制。而对于有单词环的情况,每个结点间可经过的方向及其次数等于其所对应种类的单词数量,运用深度搜索也同样能够解决相应的问题。 在搜索阶段,程序对每个节点进行深度搜索,搜索以该结点为首的最长路径,并根据不同的输入参数得出符合要求的单词链。最终通过接口完成结果的输出。
第二阶段:动态规划优化部分功能
笔者的小组在较短时间内就完成了基于深度搜索的单词链搜索程序。在通过正确性测试并进行性能测试后,不出意料的出现的运行效率低的问题。运行效率低的主要原因是原始深度遍历不具有记忆和预测功能。这会导致在进行多次遍历时会对以搜索的路径和明显不是最优解的路径进行遍历。会经过分析后,我们决定采取以下方案对程序进行优化。 对于无单词环并且不同时限定首尾的情况,废弃原先的方法转而采用动态规划的方法。这几个搜索最长单词链的问题可以分解为求解与其相连的字母的最长单词链加上两者间距离的最大值。比如正向动态规划时,求解以'a'为首的最长单词链,而'b'、'c'结点可以由'a'结点直接到达,则原问题的最优解为子问题'b'、'c'的最优解分别加上'a'到这些结点的边的权值。用公式表示为: 正向递推式:F(c1)=max{F(c1)+len(c1ci)}*或*F(c1)=max{F(c1)+len(c1ci)}
逆向递推式:f(c1)=max{f(c1)+len(cic1)}*或*f(c1)=max{f(c1)+len(cic1)}
在使用动态规划后,程序将无单词环并且不同时限定首尾的情况的计算压缩至了1s内,基本满足了要求。
第三阶段:优化深度搜索
在进行动态规划的优化之后,我们发现,动态规划虽然效率很高,但是并不能处理同时指定头和尾的问题,于是我们仿照动态规划表达式的思想,对于我们的深度优先搜索做了一步剪枝。具体的剪枝思想如下:
在搜索回退的时候,用一个数组,记录下每个字母距离其末尾的最大长度,或者最大单词个数,当第二次搜索到这个字母节点时,直接比较当前长度,当前节点距离末尾的最大长度,如果前者比后者小,直接剪掉这个分支。保证了每条边只搜索了一遍,极大的提高了深度优先搜索的效率。
性能测试:
上图为在无单词环的情况下进行的约九千单词量的性能测试结果。由图可知,在进行vector操作上的消耗较多。消耗最多的代码是在addWord函数中将新单词加入至有序单词表的操作时采用了vector的insert函数。这一消耗仅出现在预处理的部分,在保证单词表中的单词是按照长度降序排列的同时还过滤了重复的单词。该操作虽然带来了一定的损耗,但是组织好的单词表更易于计算过程的检索,降低了检索的复杂度,防止递归导致的消耗的放大。计算过程采用的动态规划算法大大减小了该阶段的消耗。
上图为在有单词环的情况下进行的性能测试。CPU的消耗主要出在递归调用的函数Dfs_solve1中。
契约式编程的优缺点
Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming, is an approach for designing software. It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants.
以上是维基百科对契约式编程的释义。有维基百科释义可知,契约式编程中,每个模块有明确的契约关系,模块互相调用的时候都有责任有义务向调用自己的模块保证使用的正确性以及自己功能的正确性。
契约编程的优缺点如下
优点:
1、程序的整体设计更为优秀
为了符合契约式编程的核心思想,编程者必须在编程之前就要有个更好的整体设计以便于能更好的完成自己的契约,保证程序的正确性。为了有更易于使用的接口,更正确的内部功能,必须有更清楚的设计,更简单的设计。
2、提高系统可靠性,鲁棒性
只要各个模块都遵守契约,那么一定程度上就大大的降低了模块间耦合时之间的Bug。程序的鲁棒性也得到了很好的提升。
3、出色的文档
契约乃是类特性的公用视图中的固有成分,契约是值得信赖的文档(运行时要检查断言,以保证制定的契约与程序的实际运行情况一致)。
缺点:
1、提高了编程的撰写成本
为了符合规范,并符合契约精神,编写的时候必须要更加注意这些细节上的内容,增加了代码编写的成本。
2、需要有很多编程经验
为了真正的实现契约式编程,对编码者的实际编程经验也有较高的要求,需要编程着有较好的编程基础以及一定的编程意识才能真正的完成契约式编程。
契约式编程的应用
在撰写类的时候,每个类都单独进行测试,封装前确保了调用时的便宜性和功能的正确性。在封装运算程序与GUI的时候也做到了 契约式编程,极大的方便了对接与测试。
计算模块部分单元测试展示
笔者对工程中Core类的正确性以及封装后的dll的正确性进行了测试
Core类测试集
TEST_CLASS(TEST)
{
public:
Core *CORE = NULL;
TEST_METHOD_INITIALIZE(initEnv)
{
CORE = new Core();
}
TEST_METHOD_CLEANUP(cleanWordList)
{
delete CORE;
CORE = NULL;
}
TEST_METHOD(Test_1) // -w
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
string ans[] = {
"ab", "bc", "cd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for(int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, , , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_2) // -c
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
string ans[] = {
"accccccccccccccccccccc", "cd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, , , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_3) // -w -h
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
};
string ans[] = {
"bc", "cd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, 'b', , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_4)// -c -h
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
"dd",
};
string ans[] = {
"bdddd", "dd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, 'b', , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_5)// -w -t
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
};
string ans[] = {
"ab", "bc"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, , 'c', false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_6)// -c -t
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
"cc"
};
string ans[] = {
"accccccccccccccccccccc", "cc"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, , 'c', false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_7)// -w -h -t
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"be",
"cd",
"ce",
"de"
};
string ans[] = {
"bc", "cd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, 'b', 'd', false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_8)// -c -h -t
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"be",
"cd",
"ce",
"de",
"dd",
};
string ans[] = {
"bdddd", "dd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, 'b', 'd', false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_9)// -w but have ring
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"db",
"be",
"cd",
"ce",
"de"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(-, CORE->gen_chain_word(*lines, *chain, , , false));
delete lines;
delete chain;
}
TEST_METHOD(Test_10)// -c but have ring
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"db",
"be",
"cd",
"ce",
"de"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(-, CORE->gen_chain_char(*lines, *chain, , , false));
delete lines;
delete chain;
}
TEST_METHOD(Test_11)// -w have self
{
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bb",
"bc",
"bdddd",
"be",
"cc",
"cd",
"ce",
"dd",
"de"
};
string ans[] = {
"ab", "bb", "bc", "cc", "cd", "dd", "de"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, , , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_12){// -c have self
string words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bb",
"bc",
"bdddd",
"be",
"cc",
"cd",
"ce",
"dd",
"de"
};
string ans[] = {
"accccccccccccccccccccc", "cc", "cd", "dd", "de"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, , , false));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_13)// -w -r
{
string words[] = {
"ab", "aaaaabbbbbccccc", "aaaaaddddd",
"bc",
"cb", "cd",
};
string ans[] = {
"aaaaabbbbbccccc", "cb", "bc", "cd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, , , true));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_14) {// -c -r
string words[] = {
"ab", "aaaccc", "aaaaabbbbbcccccddddd",
"bc",
"cb", "cd",
"dd",
};
string ans[] = {
"aaaaabbbbbcccccddddd", "dd"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, , , true));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_15) {// -w -r -h -t
string words[] = {
"ab",
"bccccccccccccccccccccccccccc", "bd",
"cd",
"da", "dc",
};
string ans[] = {
"bd", "dc", "cd", "da"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_word(*lines, *chain, 'b', 'a', true));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
TEST_METHOD(Test_16) {// -c -r -h
string words[] = {
"ab",
"bccccccccccccccccccccccccccc", "bd",
"cd",
"da", "dc",
};
string ans[] = {
"bccccccccccccccccccccccccccc","cd", "da"
};
vector<string> *lines = new vector<string>();
vector<string> *chain = new vector<string>();
for (int i = ; i < ; i++)
{
lines->push_back(words[i]);
}
Assert::AreEqual(, CORE->gen_chain_char(*lines, *chain, 'b', 'a', true));
Assert::AreEqual(size_t(), chain->size());
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
Assert::AreEqual(ans[], (*chain)[]);
delete lines;
delete chain;
}
拓扑排序测试
TEST_METHOD(TEST_TopoSort_1)
{
string str_list[] = {
"aa", "abc" , "cbd", "ddd", "da"
};
for (int i = ; i < ; i++)
{
WORDLIST->parseString(str_list[i]);
}
dpSolve = new DPSolve(WORDLIST, 'w');
Assert::AreEqual(false, dpSolve->topoSort());
}
TEST_METHOD(TEST_TopoSort_2)
{
string str_list[] = {
"aa", "abc" , "cbd", "ddd", "db"
};
for (int i = ; i < ; i++)
{
WORDLIST->parseString(str_list[i]);
}
dpSolve = new DPSolve(WORDLIST, 'w');
Assert::AreEqual(true, dpSolve->topoSort());
}
dll测试
TEST_METHOD(Test_1) // -w
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
char* chain[];
char* ans[] = {
"ab", "bc", "cd"
};
Assert::AreEqual(, gen_chain_word(words, , chain, , , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_2) // -c
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
char* chain[];
char* ans[] = {
"accccccccccccccccccccc", "cd"
};
Assert::AreEqual(, gen_chain_char(words, , chain, , , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_3) // -w -h
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
};
char* chain[];
char* ans[] = {
"bc", "cd"
};
Assert::AreEqual(, gen_chain_word(words, , chain, 'b', , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_4) // -c -h
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
"dd",
};
char* chain[];
char* ans[] = {
"bdddd", "dd"
};
Assert::AreEqual(, gen_chain_char(words, , chain, 'b', , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_5) // -w -t
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
};
char* chain[];
char* ans[] = {
"ab", "bc"
};
Assert::AreEqual(, gen_chain_word(words, , chain, , 'c', false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_6) // -c -t
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bdddd",
"cd",
"cc"
};
char* chain[];
char* ans[] = {
"accccccccccccccccccccc", "cc"
};
Assert::AreEqual(, gen_chain_char(words, , chain, , 'c', false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_7)// -w -h -t
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"be",
"cd",
"ce",
"de"
};
char* chain[];
char* ans[] = {
"bc", "cd"
};
Assert::AreEqual(, gen_chain_word(words, , chain, 'b', 'd', false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_8)// -c -h -t
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"be",
"cd",
"ce",
"de",
"dd",
};
char* chain[];
char* ans[] = {
"bdddd", "dd"
};
Assert::AreEqual(, gen_chain_char(words, , chain, 'b', 'd', false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_9)// -w but have ring
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"db",
"be",
"cd",
"ce",
"de"
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, , , false));
}
TEST_METHOD(Test_10)// -c but have ring
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bc",
"bdddd",
"db",
"be",
"cd",
"ce",
"de"
};
char* chain[];
Assert::AreEqual(-, gen_chain_char(words, , chain, , , false));
}
TEST_METHOD(Test_11)// -w have self
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bb",
"bc",
"bdddd",
"be",
"cc",
"cd",
"ce",
"dd",
"de"
};
char* chain[];
char* ans[] = {
"ab", "bb", "bc", "cc", "cd", "dd", "de"
};
Assert::AreEqual(, gen_chain_word(words, , chain, , , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_12) {// -c have self
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"ae",
"bb",
"bc",
"bdddd",
"be",
"cc",
"cd",
"ce",
"dd",
"de"
};
char* chain[];
char* ans[] = {
"accccccccccccccccccccc", "cc", "cd", "dd", "de"
};
Assert::AreEqual(, gen_chain_char(words, , chain, , , false));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_13)// -w -r
{
char* words[] = {
"ab", "aaaaabbbbbccccc", "aaaaaddddd",
"bc",
"cb", "cd",
};
char* chain[];
char* ans[] = {
"aaaaabbbbbccccc", "cb", "bc", "cd"
};
Assert::AreEqual(, gen_chain_word(words, , chain, , , true));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_14) {// -c -r
char* words[] = {
"ab", "aaaccc", "aaaaabbbbbcccccddddd",
"bc",
"cb", "cd",
"dd",
};
char* ans[] = {
"aaaaabbbbbcccccddddd", "dd"
};
char* chain[];
Assert::AreEqual(, gen_chain_char(words, , chain, , , true));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_15) {// -w -r -h -t
char* words[] = {
"ab",
"bccccccccccccccccccccccccccc", "bd",
"cd",
"da", "dc",
};
char* ans[] = {
"bd", "dc", "cd", "da"
};
char* chain[];
Assert::AreEqual(, gen_chain_word(words, , chain, 'b', 'a', true));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
TEST_METHOD(Test_16) {// -c -r -h
char* words[] = {
"ab",
"bccccccccccccccccccccccccccc", "bd",
"cd",
"da", "dc",
};
char* ans[] = {
"bccccccccccccccccccccccccccc","cd", "da"
};
char* chain[];
Assert::AreEqual(, gen_chain_char(words, , chain, 'b', 'a', true));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
Assert::AreEqual(, strcmp(chain[], ans[]));
}
以下是测试时代码覆盖率及测试结果。
计算模块部分异常处理说明
异常种类:
输入输出单词表有效性
参数(head、tail)有效性
无环情况搜索到环
未搜索到单词链
异常测试:
输入输出单词表有效性测试
TEST_METHOD(Test_1)
{
char* words[] = {
NULL,
NULL,
"ad",
"bc",
"bd",
"cd",
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, , , false));
}参数有效性测试
TEST_METHOD(Test_2)
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, '-', , false));
}
TEST_METHOD(Test_3)
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
"bc",
"bd",
"cd",
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, , '-', false));
}环识别测试
TEST_METHOD(Test_6)
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ba",
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, , , false));
}未搜索到单词链测试
TEST_METHOD(Test_4)
{
char* words[] = {
"ab",
"accccccccccccccccccccc",
"ad",
};
char* chain[];
Assert::AreEqual(-, gen_chain_word(words, , chain, , , false));
}
界面模块设计
界面模块设计主要采用C++的 QT模块,利用QT creator 先设计好主要的GUI界面之后,导出代码,然后针对每个控件编写相应的响应函数。GUI 实在64位的编译环境下编写,主要支持功能时直接输入框输入单词,和用户交互式的导入文本文件,也支持将程序运行的结果导出到用户指定文件中。
模块主要界面如下:
其中主要的功能函数绑定在openfile writefile 以及 run 三个Button 上,界面代码如下:
void gui(QMainWindow *MainWindow)
{
if (MainWindow->objectName().isEmpty())
MainWindow->setObjectName(QStringLiteral("MainWindow"));
MainWindow->resize(, );
centralWidget = new QWidget(MainWindow);
centralWidget->setObjectName(QStringLiteral("centralWidget"));
//centralWidget = MainWindow;
textBrowser = new QTextBrowser(centralWidget);
textBrowser->setObjectName(QStringLiteral("textBrowser"));
textBrowser->setGeometry(QRect(, , , ));
textBrowser_2 = new QTextEdit(centralWidget);
textBrowser_2->setObjectName(QStringLiteral("textBrowser_2"));
textBrowser_2->setGeometry(QRect(, , , ));
checkBox = new QCheckBox(centralWidget);
checkBox->setObjectName(QStringLiteral("checkBox"));
checkBox->setGeometry(QRect(, , , ));
checkBox_2 = new QCheckBox(centralWidget);
checkBox_2->setObjectName(QStringLiteral("checkBox_2"));
checkBox_2->setGeometry(QRect(, , , ));
checkBox_3 = new QCheckBox(centralWidget);
checkBox_3->setObjectName(QStringLiteral("checkBox_3"));
checkBox_3->setGeometry(QRect(, , , ));
checkBox_4 = new QCheckBox(centralWidget);
checkBox_4->setObjectName(QStringLiteral("checkBox_4"));
checkBox_4->setGeometry(QRect(, , , ));
checkBox_5 = new QCheckBox(centralWidget);
checkBox_5->setObjectName(QStringLiteral("checkBox_5"));
checkBox_5->setGeometry(QRect(, , , ));
textEdit = new QLineEdit(centralWidget);
textEdit->setObjectName(QStringLiteral("textEdit"));
textEdit->setGeometry(QRect(, , , ));
textEdit_2 = new QLineEdit(centralWidget);
textEdit_2->setObjectName(QStringLiteral("textEdit_2"));
textEdit_2->setGeometry(QRect(, , , ));
label = new QLabel(centralWidget);
label->setObjectName(QStringLiteral("label"));
label->setGeometry(QRect(, , , ));
label_2 = new QLabel(centralWidget);
label_2->setObjectName(QStringLiteral("label_2"));
label_2->setGeometry(QRect(, , , ));
pushButton = new QPushButton(centralWidget);
pushButton->setObjectName(QStringLiteral("pushButton"));
pushButton->setGeometry(QRect(, , , ));
pushButton_2 = new QPushButton(centralWidget);
pushButton_2->setObjectName(QStringLiteral("pushButton_2"));
pushButton_2->setGeometry(QRect(, , , ));
pushButton_3 = new QPushButton(centralWidget);
pushButton_3->setObjectName(QStringLiteral("pushButton_3"));
pushButton_3->setGeometry(QRect(, , , ));
MainWindow->setCentralWidget(centralWidget);
menuBar = new QMenuBar(MainWindow);
menuBar->setObjectName(QStringLiteral("menuBar"));
menuBar->setGeometry(QRect(, , , ));
MainWindow->setMenuBar(menuBar);
mainToolBar = new QToolBar(MainWindow);
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar(MainWindow);
statusBar->setObjectName(QStringLiteral("statusBar"));
MainWindow->setStatusBar(statusBar);
retranslateUi(MainWindow);
QMetaObject::connectSlotsByName(MainWindow);
} // setupUi
1、openfie
该 button 绑定了导入输入文件的函数,可以方便让用户直接导入自己的输入数据,代码实现如下:
void MainWindow::openfile()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("choose log"),"",tr("TXT(*.txt)"));
if (fileName.isEmpty())
return;
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
while (!file.atEnd())
{
QByteArray line = file.readLine();
QString str(line);
//qDebug() << str;;
this->textBrowser_2->insertPlainText(str);
}
file.close();
}
}
2、writefile
该 button 绑定了导入结果数据的函数,可以方便让用户直接导出函数的运行结果,代码实现如下:
void MainWindow::writefile()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("choose log"),"",tr("TXT(*.txt)"));
if (fileName.isEmpty())
return;
QFile file(fileName);
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
while (!file.atEnd())
{
QByteArray line = file.readLine();
QString str(line);
//qDebug() << str;;
this->textBrowser_2->insertPlainText(str);
}
file.close();
}
}
3、run
该button绑定的函数调用了Core.dll中的函数,实现了对数据的处理与计算。代码如下:
void MainWindow::run()
{
QLibrary mylib("Core.dll");
mylib.load();
//加载动态库
if (!mylib.isLoaded())
{
qDebug()<<QString("Load Oracle oci.dll failed!\n");
return ;
}
p_gen_chain_word get_chain_word=(p_gen_chain_word)mylib.resolve("gen_chain_word");
p_gen_chain_char get_chain_char=(p_gen_chain_char)mylib.resolve("gen_chain_char");
char head = '\0',end = '\0';
char* words[];
char* result[];
bool b_w = this->checkBox->isChecked()==true;
bool b_c = this->checkBox_2->isChecked()==true;
bool b_h = this->checkBox_3->isChecked()==true;
bool b_t = this->checkBox_4->isChecked()==true;
bool b_r = this->checkBox_5->isChecked()==true;
if(b_w && b_c)
{
QMessageBox:: information(NULL,"Error","can't choose w and c at the same time");
return;
}
if(!b_w && !b_c)
{
QMessageBox:: information(NULL,"Error","need a w or cs");
return;
}
if(b_h)
{
QByteArray head_text = this->textEdit->text().toLower().toLatin1();
head = head_text.data()[];
if(!(head>='a' && head <='z'))
{
QMessageBox:: information(NULL,"Error","head must in a-z");
return;
}
}
if(b_t)
{
QByteArray head_text = this->textEdit_2->text().toLower().toLatin1();
end = head_text.data()[];
if(!(head>='a' && head <='z'))
{
QMessageBox:: information(NULL,"Error","tail must in a-z");
return;
}
}
QStringList text = this->textBrowser_2->toPlainText().split("\n");
int len = ;
int word_num = String2Char(text,words);
if(b_w)
{
len = get_chain_word(words,word_num,result,head,end,b_r);
}
if(b_c)
{
len = get_chain_char(words,word_num,result,head,end,b_r);
}
if(len<=)
{
if(len == -)
{
QMessageBox:: information(NULL,"Error","shouldn't have circles");
return;
}
if(len == -)
{
QMessageBox:: information(NULL,"Error","Chain don't exist");
return;
}
QMessageBox:: information(NULL,"Error","unkown error! please input valid data");
return;
}
qDebug()<<len;
int i;
this->textBrowser->clear();
for(i = ;i<len;i++)
{
string a(result[i]);
this->textBrowser->append(QString::fromStdString(a));
qDebug()<<QString::fromStdString(a);
}
}
界面模块与计算模块对接
笔者小组同其他小组(16061161-15231112组和16061093-16061155组)进行了计算模块dll的交换。由于两个小组在项目开始前就对接口部分进行了讨论,在接口各参数、返回值以及功能上达成了共识,即计算接口设计部分所述。由于完成所有模块的设计及dll打包后,笔者在本小组的测试程序以及GUI上对dll进行了导入测试,在进入与其他小组对接阶段时并未遇到严重BUG,但在这过程中也遇到了一些小问题。
打包dll和GUI模块所支持处理器不同。 在最初打包dll时未注意X86和X64以及Debug和Release版本间的区别,打包出错误版本的dll,导致GUI无法导入dll。在复核了版本后就解决了这一问题。
异常处理问题。 笔者小组采用Qt Creater进行GUI部分的开发,而后端进行Visual Studio进行开发。前者对于dll抛出异常的支持较差,容易导致程序出错。在采用返回值返回错误信息的方式后解决了这一问题。
开发过程总览
在结对之前,笔者和结对伙伴就已经认识,但从未在程序开发上进行合作。本次结对编程应当是小组两人第一次合作开发。以下是按照项目开发的里程碑来总览笔者小组开发的历程。
启动题目 在结对题目刚出来时,我们很快就在一起对需求进行了讨论,很快就列出了最初的项目设计以及日程计划,开始了项目的推进。由于其他课程以及实验室任务等原因,我们并没有很多公共的时间进行结对开发,仅有每天晚上用大约6小时左右的时间一同编程。
完成初版 在项目启动后约两天,我们就按照最初的设计(设计见性能改进部分)实现了程序的基本功能。在实现的过程中,笔者按照单元测试的编写要求,编写并进行了单元测试,完成了第一版的测试集,供后期回归测试使用。
优化算法 由于最初的设计的时间复杂度较高,我们在完成初版的测试后便进入了算法的改进和优化的阶段。笔者将部分的功能改为使用动态规划算法实现,并根据搜索单词链的要求设计了不同的递推式。笔者的搭档将初版的深度搜索进行了优化以补充动态规划未涉及的功能。经过了约4天,我们基本完成了算法的优化,开始了新测试的编写以及进行回归测试。
GUI开发和核心代码封装 在算法优化的后半阶段,我们开始了GUI部分的同步开发以及计算模块的封装。这一部分是我们整个开发过程中最为艰难的阶段。我们在Qt环境的配置、dll的打包及使用上遇到了很多困难。主要在于dll类似于黑盒,我们在测试其是否打包成功并加载入Qt是耗费了很多时间。
终版完成 最终经过了两周的结对编程,笔者和搭档较好的完成了最长单词链的程序。在这过程中,由于是第一次结对编程前期的效率并不高。直到进入了优化阶段,我们在逐渐找到了结对编程的节奏,最终使用了约2000分钟完成了项目。
总结
结对编程优缺点
经过长达两周的结对编程,结合《构建之法》中对结对编程的描述以及这两周的亲身经历,笔者对于结对编程的优缺点有了以下的感想。
优点
更容易定位Bug 结对编程过程中,两个人共同看一份代码,极大的解决了两个人分工合作导致的代码bug 定位困难的问题,两个人在一起编程,总有一个人可以快速的定位到bug 的位置。
可以设计出更好的方法 俗话说三个臭皮匠,顶个诸葛亮,在结对编程中,两个人一起思考算法,总结出的解决问题的方法往往会比一个人想出来的更严谨也更高效。让程序的整体性能上也得到了提升。
沟通方便 结对编程,两个人坐在一起编程,沟通起来也是十分的方便,不仅很好的避免了两个人在沟通上容易产生的误解,也极大的减少了沟通所用的时间,很大程度上节省了不必要的争执,与误解导致的时间上的狼浪费。
缺点
进度会被减慢 因为结对开发不是两个人的并行开发,所以一定程度上的减慢了项目整体推进的效率,导致整个工作周期被大大的延长。
结对编程反思
经过了两周的合作,笔者对自己和队友在结对编程中各自的亮点和不足进行了一下总结。
队员 | 优点 | 不足 |
---|---|---|
笔者 | 1.吃苦耐劳 2.善于接受新知识 3.积极沟通 | 不善于撰写报告,以及进行程序测试 |
队友 | 1.代码编写规范 2.善于写测试用例 3.善于封装程序 | 不能熬夜 |
[2019BUAA软件工程]结对作业的更多相关文章
- [2019BUAA软件工程]结对编程感想
结对编程感想 写在前面 本博客为笔者在完成软件工程结对编程任务后对于编程过程.最终得分的一些感想与经验分享.此外笔者还对于本课程的结对编程部分提出了一些建议. Tips Link 作业要求博客 2 ...
- 软件工程结对作业01 psp表格
- [BUAA软工]第一次结对作业
[BUAA软工]结对作业 本次作业所属课程: 2019BUAA软件工程 本次作业要求: 结对项目 我在本课程的目标: 熟悉结对合作,为团队合作打下基础 本次作业的帮助:理解一个c++ 项目的开发历程 ...
- [2019BUAA软件工程]第1次阅读作业
[2019BUAA软件工程]第1次阅读作业 Tips Link 作业连接 [2019BUAA软件工程]第1次阅读作业 读<构建之法>的疑惑 个人开发流程(Personal Software ...
- 11061160_11061151_Pair Project: Elevator Scheduler软件工程结对编程作业总结
软件工程结对编程作业总结 11061160 顾泽鹏 11061151 庞梦劼 一.关于结对编程 这次的软工任务既不是单打独斗的个人任务,也不是集思广益的团队项目,而是人数为两人的结对编程.两个人合 ...
- BUAA软件工程结对项目作业
BUAA软件工程结对项目 小组成员:16005001,17373192 1.教学班级和项目地址 项目 内容 这个作业属于哪个课程 博客园班级连接 这个作业的要求在哪里 结对项目作业 我在这个课程的目标 ...
- BUAA 2020 软件工程 结对项目作业
Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...
- 软件工程第三次作业-结对作业NO.1
第一次结对作业 结对人员: 潘伟靖 170320077 张 松 170320079 方案分析 我们对所供的资料进行分析,如下: 从提供的资料可以看出,需要解决的问题以及满足的需求主要有两类目标用户,各 ...
- 结对作业——随机生成四则运算(Core 第7组)
结对作业 ——随机生成四则运算(core第7组) 吕佳玲 PB16060145 涂涵越 PB16060282 GITHUB地址 https://github.com/hytu99/homework_2 ...
随机推荐
- Centos 7 squid 用户认证
一.安装安装过程十分简便,只需要安装一下squid,一条命令搞定yum install squidrpm -qa | grep squidsquid-3.5.20-2.el7_3.2.x86_64 二 ...
- 华为S5700配置端口镜像和华三S5120配置802.1X认证记录
一.说明 事情的起因是我们部门有个华为的S5700交换机,想配置端口镜像抓包但让助理买的串口线很久都还没到:而昨天测试部的同事说他们那有台华三的S5120想要配802.1X认证,但只有华为交换机的文档 ...
- 跟随我在oracle学习php(19)
Order by子句 形式: order by 排序字段1 [排序方式], 排序字段2 [排序方式], ..... 说明: 对前面取得的数据(含from子句,where子句,group子句, ...
- java按照指定格式输出系统时间使用SimpleDateFormat方法
public class TimeThree { public static void main(String[] args) { SimpleDateFormat d = new SimpleDat ...
- UVa LA 4094 WonderTeam 构造 难度: 1
题目 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_pr ...
- 二、linux的安装
1. 虚拟机安装: 1.1. 什么是虚拟机 虚拟机:一台虚拟的电脑. 虚拟机软件: * VmWare :收费的. * VirtualBox :免费的. 1.2. 安装VmWare 参考<虚拟软件 ...
- 较为复杂的实现Parcelable的子类--推荐Android中使用
2017-08-14 21:23:34 一个较为复杂的Parcelable实现类 public class CommentShareBean implements Parcelable { /** * ...
- [Leetcode 40]组合数和II Combination Sum II
[题目] Given a collection of candidate numbers (candidates) and a target number (target), find all uni ...
- 14. Longest Common Prefix ★
题目内容:Write a function to find the longest common prefix string amongst an array of strings 题目分析:本题目利 ...
- 在servlet中跳转问题
跳转有重定向和转发 1重定向 2转发