结对编程项目-最长英语单词链
项目 | 内容 |
---|---|
这个作业属于哪个课程 | 2022 年北航敏捷软件工程 |
这个作业的要求在哪里 | 结对编程项目-最长单词链 |
我在这个课程的目标是 | 学习软件工程相关知识,提高自己的代码能力与团队协作能力。 |
这个作业在哪个具体方面帮助我实现目标 | 通过两个人的团队作业提高自己的协作能力与表达水平,在实践中锻炼需求分析、测试与文档编写能力。 |
1. 项目说明
- 教学班级:周五班
- 项目地址:https://github.com/ericaaaaaaaa/BUAA_SE_homework.git
2. 计划
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | |
· Estimate | · 估计这个任务需要多少时间 | 15 | |
Development | 开发 | 790 | |
· Analysis | · 需求分析 (包括学习新技术) | 40 | |
· Design Spec | · 生成设计文档 | 50 | |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 40 | |
· Design | · 具体设计 | 90 | |
· Coding | · 具体编码 | 360 | |
· Code Review | · 代码复审 | 60 | |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | |
Reporting | 报告 | 85 | |
· Test Report | · 测试报告 | 40 | |
· Size Measurement | · 计算工作量 | 15 | |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | |
合计 | 890 |
3. 教材阅读
Information Hiding
Information hiding 也称作信息隐藏,是将计算机程序中可能发生变化的部分与其它部分隔离开,从而让程序在该部分被修改时不会被大幅修改。信息隐藏通常的实现方法是通过提供接口。本次作业完成中我们在第二阶段中将各个参数对应的方法封装到 Core
类中,再提供四种接口,实现信息隐藏。
int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
int gen_chains_all(char* words[], int len, char* result[]);
int gen_chain_word_unique(char* words[], int len, char* result[]);
int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
Interface Design
Interface design 主要面向用户,强调给用户一个方便使用的接口。本次作业中我们设计了 GUI 图形界面,方便用户更直观的使用程序。此外,同上文中 Information Hiding 所述,本次作业抽象出 Core 类的接口方便用户调用。
Loose Coupling
Loose coupling,又称松耦合,是紧耦合的对立面。实现松耦合可以降低组件之间的关联性,将其相互影响降到最低。可实现松耦合的对象包括类、接口、数据和服务等。在本次结对编程中,我们将 GUI 设计与核心计算模块 Core 分离,将 Core 生成动态链接库 dll,并用 GUI 直接调用 dll 完成功能的运行,实现了运算与展示部分的松耦合。此外,我们的单元测试模块也通过调用 dll 实现测试与运算的松耦合。
附加题
本组与周子颖(19373132)、王宇欣(19373349)交换了 dll 模块,并分别进行了测试,测试方式为:
- 本组单元测试模块 + 对方组的 dll
- 本组的 GUI + 对方组的 dll
- 本组的 dll + 对方组测试模块
- (由于在互换时对方组没有设计 GUI,故无法实现本组的 dll + 对方组 GUI)
在互换 dll 时用了不少时间统一接口设计,但在互换后也依然存在部分不统一的地方(如对方组输入给 dll 的单词保证为小写,本组则在 dll 中处理大小写等)
互换测试中发现对方组的 bug 有:
Core 中未解决单词链中单词不允许相同的情况
输入单词:
aaaa, aaaa, aaaaa
参数:-w, -r
输出:
Core 无法处理部分合法输入,异常退出
输入:
"ab", "cbc", "dcd", "ede", "faf", "gfg", "hgh"
参数:-m
输出:异常退出(exit code -529697949)
4. 模块接口设计与实现
-n
算法设计:采用 DFS 的方式实现,每次求出以某一特定字母开头的全部可能单词链。
无环路
alphatet
中每一格存首尾相同的单词组
判断环路
算法设计
采用深度搜索,将首尾相同的单词都视作同一个边,用CycleFind
记录每轮递归找到的点,同时找到的点都放入AllFind
中,如下如分析,如果在单词深度搜索中没有找到导致环路的点,那么这一轮所有出现过的点都不用再分析了,因为假设再用它为开头找环,如果能够找到环,则在它之前的那一轮循环中也能找到环,这样就出现矛盾,因此用AllFind
排除一些重复的点,并且将未知单词个数简化到最多26个点。
-w
算法设计
和环路算法数据结构相同,同样视首尾相同的单词为同一条线,换成迪杰斯特拉算法找所有源的最大长度路径,由于无环,首先排除入度不为0的字母,循环遍历只出不进的字母,每一轮找到离该源点最大路径的点,经过一次松弛,记录前项点,其中避开a---a
格式的点,保证除了初始点,前项点始终与自己不同,循环所有点后,再进行下一个单源点寻找。在计算最长路径的时候,需要通过前项点寻找路径,同时,假设找到的点为a,如果alphabet中存在a---a
格式的点,则需要将路径再添加一次权重,如果长度超过现有最大值则更新。
只有-t时,反转alphabet[colomn][row]
的colomn\row
去寻找
权重为1
-c
算法设计
上述算法中权重为单词长度
-m
算法设计
上述算法中寻找路径时跳过对a---a
格式的点的判断
有环路
-w & -c
算法设计
在有环路的情况下,统一采用 DFS 的方式进行检索,每在单词链中添加一个单词即将其标记为 dirty,选取最长的单词链保存。
性能改进
由于 -c 要求选出字符长度最长的单词链,因此在输入时可根据单词长度对其排序,每次从最长单词开始遍历,可以减少对于单词图的遍历次数,获得一定的性能提升。
5. UML 图
UML 图如下:其中,图上方 Word
是用来保存单词(长度 & 内容),WordList
, Core
为主要计算模块,图中右侧 UiForm
, QWidget
, Form
是 GUI 实现中用到的类,图中下方的 Exception
是异常处理时需要用到的结构。
6. 模块接口部分性能改进
在测试中-w和-c测试量相同,其中性能测试样例包含四点全向图,多链,固定首尾。分析函数性能,Core::gen_chain_word
和 Core::gen_chain_char
所封装的 FunctionC_R
和 FunctionC_R
占据了极大的比例。其中 FunctionC_R
更多,是因为该函数需要获取单词的长度作为权重,在查单词长度时导致了这一消耗。
- 改进思路1:通过加入对点的判断,减少不必要的点的函数递归调用
- 改进思路2:有参数
-c
时先对单词长度进行排序,再执行算法 - 改进思路2:引入编译优化
7. Design by Contract, Code Contract
Design by Contract
Design by contract,又称契约式设计,是软件工程中常用的方法,要求设计者为软件提供正式、精确且可验证的接口。
本次设计中我们在实现之前先分析了需求,并写出了设计文档,结合老师博客中提供的接口,定义数据结构和函数等。在写代码时参照设计文档完成设计,实现了契约式设计。
Code Contract
Code contract 是微软采用的一种设计方法,称为程式码合约(代码协定),要求在代码中指定前置条件,后置对象和对象固定的方法。
- 前置条件是输入方法或属性时必须满足的要求
- 后置方法描述在方法或属性代码退出时的预期
- 对象固定描述处于良好状态的类的预期状态
在制定设计文档时,我们设想了功能模块的运行流程,从而将任务分解,由不同的函数分别完成。如在接受输入时:
initAlphabet
:- 初始化单词表
- 前置条件:无
- 后置方法:得到初始化后的单词表
- 异常处理:无
analyzeParam
:- 分析参数
- 前置条件:初始化后的单词表
- 后置方法:各参数变量填入相应内容
- 异常处理:参数不合法(缺失,重复,冲突,……)
readWordFromFile
:- 从文件中读入单词
- 前置条件:初始化后的单词表、合法的参数
- 后置方法:填充好单词的单词表
- 异常处理:单词数量超出限制
8. 单元测试
单元测试覆盖率如下:
8.1 测试函数说明
init(char** inputw,int inLenw)
:将测试用例导入;
void check(int refLen,char *refAns[])
:将参考结果导入,如果refLen是-1,则默认跳过assert
核对环节;
void init(char** inputw,int inLenw) {input = inputw;inLen = inLenw;
}
void check(int refLen,char *refAns[]) {cout << len << endl;if (true) {for (int i = 0; i < len; i++) {const char* tmpRes = result[i];cout << tmpRes << endl;}}else {assert(refLen == len);for (int i = 0; i < len && i < refLen; i++) {const char* tmpRef = refAns[i];const char* tmpRes = result[i];assert(strcmp(tmpRef, tmpRes) == 0);cout << tmpRes << endl;}}cout << "--------times " << times << " end ----" << endl;times++;len = 0;
}
8.2 正确性测试
构造思路如下
对于所有
- 无链
- 传入的 input 数列是否为空
- 有链
- 单链
- 是否有形如
axxxxa ax
的词组
- 是否有形如
- 多链
- 是否有会被重复使用到的单词如
"ab"之于da ca
- 是否有会被重复使用到的单词如
- 单链
- 有环
W/C
- -r 带环
- -h 有参数,以该单词为首,是否存在链
- -t 有参数,以该单词为首,是否存在链
- 样例中存在一个以 word 为标准的最长链和一个以 char 为标准的最长链
/*-------M/N-------*/
const int inLenN00 = 0;
const char* inputN00 = NULL;
init((char**)inputN00, inLenN00);len = Core::gen_chains_all(input, inLen, result);
check(-1,NULL);const int inLenN0 = 4;
const char* inputN0[inLenN0] = { "aend", "OF", "the", "World" };
const int refLenN0 = 1;
const char* refAnsN0[refLenN0] = { "thea aend" };
init((char** )inputN0, inLenN0);
len = Core::gen_chains_all(input, inLen, result);
check(refLenN0, (char**)refAnsN0);const int inLenN1 = 10;
const char* inputN1[inLenN1] = { "ab", "bc", "cd", "de", "af", "fg", "gh", "aa", "kk", "lmopq" };
init((char**)inputN2, inLenN2);
len = Core::gen_chains_all(input, inLen, result);
check(-1,NULL);
len = Core::gen_chain_word_unique(input, inLen, result);
check(-1,NULL);const int inLenM0 = 10;
const char* inputM0[inLenM0] = { "ab", "bc", "cd", "de", "af", "fg", "gh", "aa", "kk", "lmopq" };
const int refLenM0 = 4;
const char* refAnsM0[refLenM0] = {"ab","bc","cd","de"};
init((char**)inputM0, inLenM0);
len = Core::gen_chain_word_unique(input, inLen, result);
check(refLenM0, (char**)refAnsM0);/*----------W/C-----------*/
const int inLenW3 = 2;
const char* inputW3[inLenW3] = { "aaaaaaaaa","aa" };
init((char**)inputW3, inLenW3);
len = Core::gen_chain_word(input, inLen, result, 'b', 0, true);
check(0, NULL);
len = Core::gen_chain_word(input, inLen, result, 'a', 'b', true);
check(-1, NULL); len = Core::gen_chain_char(input, inLen, result, 'b', 0, true);
check(0, NULL);
len = Core::gen_chain_char(input, inLen, result, 'a', 'b', true);
check(-1, NULL);const int inLenW2 = 4;
const char* inputW2[inLenW2] = { "aaaa","abbb","bccc","cccca"};
init((char**)inputW2, inLenW2);
len = Core::gen_chain_word(input, inLen, result, 'b', 'c', true);
check(-1, NULL);len = Core::gen_chain_char(input, inLen, result, 'b', 'c', true);
check(-1, NULL);
8.3 鲁棒性测试
构造思路:
对于所有
传入的单词大小写
-n, -m, -w_noR, -c_noR
- 有环
-h, -t
- 参数不是单词字母
result长度超过 20000
/*----------M/N-----------*/const int inLenN3 = 6;
const char* inputN3[inLenN3] = { "ba", "ab","ca","ammmmmc","cb","bc"};
init((char**)inputN3, inLenN3);len = Core::gen_chains_all(input, inLen, result);
check(-1,NULL);
len = Core::gen_chain_word_unique(input, inLen, result);
check(-1,NULL);len = Core::gen_chain_char(input, inLen, result, 'b', 0, false);
check(0, NULL);
len = Core::gen_chain_char(input, inLen, result, 'a', 'b', false);
check(-1, NULL);/*----------W/C-----------*/
const int inLenW3 = 2;
const char* inputW3[inLenW3] = { "aaaaaaaaa","aa" };
init((char**)inputW3, inLenW3);len = Core::gen_chain_word(input, inLen, result, '$', 0, true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 0, 0, false);
check(-1, NULL);len = Core::gen_chain_char(input, inLen, result, '$', 0, true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 0, false);
check(-1, NULL);const int inLenW0 = 12;
const char* inputW0[inLenW0] = { "XY","YX","XZ","ZX","YZ","ZY","ax","ay","az","za","ya","xa" };
init((char**)inputW0, inLenW0);len = Core::gen_chain_word(input, inLen, result, 0, 0,false);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 0, false);
check(-1, NULL);
//SET RESULT CONTAIN = 10
len = Core::gen_chain_word(input, inLen, result, 0, 0,true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 0, true);
check(-1, NULL);
8.4 性能测试
构造全连接图测试有环检验最长
const int inLenW0 = 12;
const char* inputW0[inLenW0] = { "XY","YX","XZ","ZX","YZ","ZY","ax","ay","az","za","ya","xa" };
init((char**)inputW0, inLenW0);len = Core::gen_chain_word(input, inLen, result,0,0,true);
check(-1,NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 0, true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 'h', 0, true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 0, 't', true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 'y', 0, true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 0, 'y', true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 'x', 'y', true);
check(-1,NULL);
len = Core::gen_chain_word(input, inLen, result, 'x', 't', true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 'h', 'y', true);
check(-1, NULL);
len = Core::gen_chain_word(input, inLen, result, 'a', 0, true);
check(-1, NULL);len = Core::gen_chain_char(input, inLen, result, 'h', 0, true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 't', true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 'y', 0, true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 0, 'y', true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 'x', 'y', true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 'x', 't', true);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 'h', 'y', true);
check(-1, NULL);
check(-1, NULL);
len = Core::gen_chain_char(input, inLen, result, 'a', 0, true);
check(-1, NULL);
9. 异常处理
在计算模块中处理一下几种异常,其余放入 GUI 模块中处理。
-n,-m,输入单词文本带有循环
const int inLenN3 = 6; const char* inputN3[inLenN3] = { "ba", "ab","ca","ammmmmc","cb","bc"}; init((char**)inputN3, inLenN3);len = Core::gen_chains_all(input, inLen, result); check(-1,NULL); len = Core::gen_chain_word_unique(input, inLen, result); check(-1,NULL);len = Core::gen_chain_word(input, inLen, result, 'b', 0, false); check(0, NULL); len = Core::gen_chain_char(input, inLen, result, 'a', 'b', false); check(-1, NULL);
-h,-t后输入的字符不是字母
const int inLenW3 = 2; const char* inputW3[inLenW3] = { "aaaaaaaaa","aa" }; init((char**)inputW3, inLenW3);len = Core::gen_chain_word(input, inLen, result, '$', 0, true); check(-1, NULL); len = Core::gen_chain_char(input, inLen, result, '$', 0, true); check(-1, NULL);
20000
const int inLenW0 = 12; const char* inputW0[inLenW0] = { "XY","YX","XZ","ZX","YZ","ZY","ax","ay","az","za","ya","xa" }; init((char**)inputW0, inLenW0);//SET RESULT CONTAIN = 10 len = Core::gen_chain_word(input, inLen, result, 0, 0,true); check(-1, NULL); len = Core::gen_chain_char(input, inLen, result, 0, 0, true); check(-1, NULL);
10. GUI 设计
利用 Qt 进行图形化界面设计。
10.1 需求分析
- 输入
- 导入单词文本文件
- 直接在界面上输入单词并提交
- 参数
- 用户可选择参数(-n -w -m -c -h -t -r)
- 输出
- 将结果输出到界面上
- 提供导出按钮,将文件保存到指定位置
- 异常
- 异常情况返回提示信息
10.2 Qt Design 窗口设计
在 Qt Design 中进行窗口设计
10.3 代码生成
利用 Qt 中提供的 uic 工具将上一步中生成的 .ui 文件转化为 .h 文件。
10.4 加入功能有关逻辑
输入
界面输入
可在位于界面左上角的文本框中输入内容,点击“清空”按钮,文本框中的内容会被清空。
导入文件
点击“导入文本”,可以实现文本导入。
参数设置
用户可以勾选需要的参数。特别的,对于 -h
和 -t
,用户在勾选后可以在右方输入框内填入首字母 / 尾字母,如图。(若不勾选 -h
或 -t
,窗口中的内容视为无效)
输出
窗口输出
用户点击“运行“后,可将运行结果输出至下方文本框中。
导出文本
点击“导出文本”,弹出保存文件窗口,可以将输出(下方文本框的内容)保存至用户选定的位置(覆盖 / 新建文本文件)。
10.5 加入异常处理逻辑
输入异常
当导入文件不以
.txt
结尾时,在输出框中报出异常,如图。当导入文件中的单词数量(包括重复)大于 20000 时,会报出异常,如图
参数异常
参数冲突
当发生参数冲突时,点击”运行“会在下方文本框中报出相应错误信息,如图:
参数不能同时出现
没有功能性参数(-n
, -w
, -c
, -m
)
参数长度不合法
当 -h
或 -t
中传入参数长度不为 1 时,会在下方输出框中报出该错误。
参数内容不合法
当参数不是合法的英文字母时,会在下方输出框中报错,如图。
运行异常
代码存在环路(但无 -r 参数)
10.6 加入其它逻辑
由于运行需要一定时间,在运行时输入文本框如果改变,可能对运行结果的正确性产生一定的影响,因此在点击”运行“后,会先将输入文本框设为只读,并在运行结束后恢复。
11. 模块连接
用 dll 连接图形界面与计算核心。
11.1 生成 dll
用类 Core 封装接口。
提取接口
修改源文件中的函数,将其改为适应给定接口的函数:
int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
int gen_chains_all(char* words[], int len, char* result[])
int gen_chain_word_unique(char* words[], int len, char* result[])
int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
建立 Core 类
建立 Core 类,并将上述接口封装入 Core 类中。
其中
Core.cpp 中给出各接口的实现
#include "Core.h"int Core::gen_chain_word(char** words, int len, char** result, char head, char tail, bool enable_loop) {...}int Core::gen_chains_all(char** words, int len, char** result) {...}int Core::gen_chain_word_unique(char** words, int len, char** result) {...}int Core::gen_chain_char(char** words, int len, char** result, char head, char tail, bool enable_loop) {...}
Core.h 中给出类和接口的定义
#pragma once #ifndef CORE_H #define CORE_H #include "word.h" #include "inputProcess.h" #include "exceptionPackage.h" #endif // CORE_H using namespace std;extern "C" class __declspec(dllexport) Core {public://words为输入的单词列表,len为单词列表的长度,result存放单词链,函数返回值为单词链长度。//1.单词数量最多的最长单词链://head和tail分别为单词链首字母与尾字母约束(如果传入0,表示没有约束),//当enable_loop为true时表示允许输入单词文本中隐含“单词环”static int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop);//2.函数返回所有符合定义的单词链,函数返回值为单词链的总数static int gen_chains_all(char* words[], int len, char* result[]);//3.函数返回首字母不同的,单词数最多的单词链,函数返回值为单词链的长度static int gen_chain_word_unique(char* words[], int len, char* result[]);//4.计算最多字母数量的最长单词链static int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop); };
生成 dll
11.2 导入 dll 模块与函数
方法
在 GUI 中导入 dll 模块和相关函数,代码如下:
// 定义函数类
typedef int(*functionN)(char* words[], int len, char* result[]);
typedef int(*functionM)(char* words[], int len, char* result[]);
typedef int(*functionW)(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
typedef int(*functionC)(char* words[], int len, char* result[], char head, char tail, bool enable_loop);
// ...// 导入 dll 模块
int main(int argc, char* argv[]) {HMODULE hModule;// 将 dll 模块导入 hModule 中hModule = LoadLibrary(TEXT("../bin/core.dll")); // 将 dll 放入 bin 目录下if (hModule == nullptr) { // 若导入失败cout << GetLastError() << endl; // 打印最近一条错误信息cout << "dll doesn't exist" << endl; // 输出错误提示return 0;}// 导入 dll 函数// 根据函数名导入functionC C = (functionC)GetProcAddress(hModule, "gen_chain_char");functionW W = (functionW)GetProcAddress(hModule, "gen_chain_word");functionN N = (functionN)GetProcAddress(hModule, "gen_chains_all");functionM M = (functionM)GetProcAddress(hModule, "gen_chain_word_unique");
}
常见错误
在实际操作中,出现了部分 bug,记录如下:
动态库生成
- vs 配置和平台的选择:debug x86 还是 debug x64。(error 173)
- 生成时是否需要预编译头 pch.h(如果完全从零开始造可以先忽略,如果不是,.h文件的顺序可能需要调整)
- 生成的名称,如果是函数,尤其是通用的函数,可以通过 extern “C” 来保证函数名就是 dll 生成的名字
动态库使用
有两轮获取指针,一个是
HMODULE hModule = LoadLibrary(_T("core.dll"));
, 动态库 dllmain 中定义的一个入口,一个是functionC C = (functionC)GetProcAddress(hModule,"gen_chain_char");
,functionC 是参数组的指针类型,这两个其中一个得到了 NULL 的值,都会导致 dll 调用失败HMODULE hModule
失败的原因可能是是 vs 平台选择错误或者传入地址填写错误,或与 Unicode、LPCSTR、string 等格式类型有关
GetProcAddress
- 返回 NULL:error = 127
- dll 生成函数名已经出现错误了,不是源文件中的 那个函数名。注:查看 dll 函数名工具:Dependency Walker。
11.3 与 GUI 按键连接
main
函数中的 QWidget::connect
函数可以设置按键对应的响应函数,编写方式如下:(只保留框架)
// 运行代码
QWidget::connect(createUi.pushButton, &QPushButton::clicked, [&]{// 获取输入words = ...;result = ...;wordCount = ;// 运行代码if (...) {length = N(words, wordCount, result);// 判断返回值的合法性} else if () {...}// 输出结果...
}
到此,已实现了 GUI 和计算模块的连接。
12. 结对过程
结对采用了线上与线下结合的方式,截图 & 照片如下:
13. 结对优缺点
结对编程优缺点
优点
- 一起编码便于更好的发现 bug(很多时候在写的过程中就发现了错误)
- 可以相互学习,相互鼓励
缺点
- 需要找到两个人都空闲的时间,可工作的时间缩短
- 代码风格可能存在不一致的情况
每个人的优缺点
姓名 | 优点 | 缺点 |
---|---|---|
王雯清 | 有过 Qt 编写的经验、善于给函数命名、总结排版 | 不熟悉 visual studio |
彭琳芝 | 沟通能力强、有责任心、善于查找资料,解决疑难问题 | 不熟悉 visual studio |
14. 实际花费时间
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 10 |
· Estimate | · 估计这个任务需要多少时间 | 15 | 10 |
Development | 开发 | 790 | 1300 |
· Analysis | · 需求分析 (包括学习新技术) | 40 | 480 |
· Design Spec | · 生成设计文档 | 50 | 40 |
· Design Review | · 设计复审 (和同事审核设计文档) | 30 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 40 | 10 |
· Design | · 具体设计 | 90 | 90 |
· Coding | · 具体编码 | 360 | 360 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 240 |
Reporting | 报告 | 175 | 160 |
· Test Report | · 测试报告 | 40 | 30 |
· Size Measurement | · 计算工作量 | 15 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 120 | 120 |
合计 | 980 | 1470 |
结对编程项目-最长英语单词链相关推荐
- [2022软工第三次作业]结对编程项目——最长英语单词链
项目 内容 本作业所属课程 2022年北航敏捷软件工程教学实践 本作业要求 结对编程项目-最长英语单词链 个人课程目标 学习到软件工程的方法论,了解整个过程,并进行亲自实践 本作业在哪个具体方面帮助我 ...
- 结对编程项目——最长英语单词链
目录 结对编程项目--最长英语单词链 1. 项目地址 2. PSP 表格记录花费的时间 3. UML 图 4. 计算模块接口的设计与实现过程 5. 参考资料中 Information Hiding.I ...
- 北航2022软件工程第三次作业——结对编程(最长英语单词链)
软件工程第三次结对编程作业 项目 内容 这个作业属于哪个课程 北京航空航天大学2022春季软件工程(罗杰 任健) 这个作业的要求在哪里 结对编程项目-最长英语单词链 我在这个课程的目标是 学习软件工程 ...
- 结对项目-最长英语单词链
文章目录 结对项目-最长英语单词链 项目信息 PSP 表格 接口设计参考理念 Information Hiding Interface Design Loose Coupling **计算模块接口的设 ...
- 软件工程结对项目- 最长英语单词链
项目 内容 这个作业属于哪个课程 2023年北航敏捷软件工程 这个作业的要求在哪里 结对项目-最长英语单词链 我在这个课程的目标是 学习现代化的软件开发方法 这个作业在哪个具体方面帮助我实现目标 对结 ...
- 结对项目-最长英语单词链-20373974阮正浩
项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 结对项目-最长英语单词链 我在这个课程的目标是 学习软件工程的一般方法并实践 这个作业在哪个具体方面帮助我实现目标 实践结对编程方法, ...
- 结对项目——最长英语单词链
项目 内容 这个作业属于哪个课程 https://bbs.csdn.net/forums/buaa-ase2023 这个作业的要求在哪里 https://bbs.csdn.net/topics/613 ...
- 「软工结对编程」:最长英语单词链
项目 内容 这个作业属于哪个课程 2023年北航敏捷软件工程社区 这个作业的要求在哪里 结对项目-最长英语单词链 我在这个课程的目标是 学习有关软件开发的方法论,熟悉基本的软件开发流程,通过" ...
- 2023软工第三次作业-最长英语单词链
结对项目-最长英语单词链 项目 内容 这个作业属于哪个课程 2023北航软件工程 这个作业的要求在哪里 结对项目-最长英语单词链 我在这个课程的目标是 帮助我初步建立软件工程敏捷开发的整体流程和概念, ...
最新文章
- Lua截取utf-8编码的中英文混合字符串
- Eclipse安装spring tool suite(4.9.0版本)
- Nacos系列:Nacos的三种部署模式
- 《社交网站界面设计(原书第2版)》——2.10 自我反省式的出错信息
- XamarinAndroid组件教程RecylerView适配器设置动画
- 阿里巴巴Java开发规约插件p3c
- 每一个程序员都应该知道的高并发处理技巧、创业公司如何解决高并发问题、互联网高并发问题解决思路、caoz大神多年经验总结分享...
- 生成树(STP)学习笔记
- Centos7下安装netstat
- 如何以 JAVA call 一個現有的 dll 檔?
- eigen 列拼接_R语言-强大的矩阵运算
- Python中的异常处理try、exception、raise
- append从一个添加到另一_麻城一总投资1.5亿项目开工建设,另一项目预计9月底开工,还有一个年产值14亿的5G项目快速推进中...
- 终于注册csdn博客了!
- Properties 类的详细使用(十三)
- Zookeeper的事务--Transaction
- IO流的详解,彻底了解IO流
- configure: error: no acceptable C compiler found in $PATH 问题解决
- Mysql可视化软件-Navicat和SQLyog
- pandas 如何删掉第一行_pandas删除指定行详解
热门文章
- python自学笔记
- mysql和虚拟主机区别_mysql和虚拟主机的区别是什么
- android复读机功能,点滴复读机最新版-点滴复读机app下载v3.0.1 安卓版-腾牛安卓网...
- python 中画球体_python – 有效地绘制许多球体
- 资本资产定价模型简介-多因子寻找Alpha统计套利
- 中国人民大学 计算机应用技术,中国人民大学计算机应用技术考研经验-人大信息学院考研辅导班...
- workbench焊接实例_[转载]Workbench的焊接模拟过程(高斯移动热源)
- 2023 上海(深圳)国际导热散热材料及设备展览会
- 《涨知识啦24》---JBS or MPS?
- 我的世界java额外参数_我的世界超平坦自定义部分参数