对对碰 -- 软工结对编程博客

2. 预估PSP表格

在开始实现程序之前,在下述 PSP 表格记录下你估计将在程序的各个模块的开发上耗费的时间 14.你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 10 20
Development 开发
· Analysis · 需求分析 (包括学习新技术) 400 500
· Design Spec · 生成设计文档 100 100
· Design Review · 设计复审 (和同事审核设计文档) 50 60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 100 120
· Coding · 具体编码 400 500
· Code Review · 代码复审 50 100
· Test · 测试(自我测试,修改代码,提交修改) 300 450+50=500
Reporting 报告
· Test Report · 测试报告 20 30
· Size Measurement · 计算工作量 5 5
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 30
合计 1485 1995

3. Information Hiding,Interface Design,Loose Coupling

看教科书和其它资料中关于 Information Hiding,Interface Design,Loose Coupling 的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的

Information Hiding

The concept of information hiding was first described by David Parnas in Parnas (1972).

Written another way, information hiding is the ability to prevent certain aspects of a class or software component from being accessible to its clients, using either programming language features (like private variables) or an explicit exporting policy.

---- From WikiPedia_Information_Hiding

信息隐藏能够能够隐藏程序的复杂度,代码模块应该采用定义良好的接口来封装,其内部结构是外部是不可见的。

具体措施来进行信息隐藏:

  • 对象的封装保护:
    • 类中的attribute定义为private类型,通过结构方法进行访问或设置;
    • 对外只暴露出输入输出功能和计算功能;
  • 信息集中:
    • 减少不明数字,即使用静态常量来代替直接出现的数字,便于对这些数字进行改动,保证正确性。如我们将经常使用的eps和-eps设置为静态常量。
    • 定义强枚举类型,防止不明数字的出现,提高程序正确性。

Interface Design

OO设计五大原则之

SRP(Single Responsibility Principle 单一职责原则)

DIP(Dependence Inversion Principle 依赖倒置原则)

ISP(Interface Segregation Principle 接口分隔原则)

对于我们来说,主要要实现了以下的部分:

  • 接口的设计要简洁:
    • 只保留需要的方法。在开始设计的过程中我们实现的API中有很多冗余方法,后续过程中由于这些方法可能会影响程序的安全性,因此我们对接口的方法进行了简化。
  • 接口单一职责,在使用的过程中不用担心因为方法的调用导致其他的后果。

Loose Coupling

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.

---- From WikiPedia_Loose_coupling

松耦合指一个代码单元无需其他单元的配合即可使用,就像更换PC的部件那样。

在我们的项目中,我们实现了计算模块和UI模块的松耦合:二者之间以某种协议的基本类型的数据流相互传递数据。在对计算模块进行更改之后,我们只要确保协议的基本数据流不变,便能保证整体实现的功能不变。

4. 计算模块接口的设计与实现过程

计算模块接口的设计与实现过程 设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处

在结对的开始,结对的两位同学根据各自的个人项目作业对本项目的计算模块设计进行了统一:核心部分为4个类,包含圆和线两个部分,其中射线、线段等均是线的子类。各个类具有和其他对象的交点计算函数。

实现流程:

  1. 排除平行线或重合线:不同于上次作业,我们不能直接排除对于两条平行的线(射线、线段),因为两条共线的射线也可能存在交点。因此对于共线的线,我们需要进行额外的判断(如图情况)。

  1. 排除平行线后,我们面对的问题便是一般问题,为了简化逻辑,本次项目的实现逻辑基本参照上次作业,即射线、线段间的交点也使用直线的交点计算公式。在算出交点后,再判断该交点是否能够是可行的,并舍弃非法点。在设计的过程中,我们也考虑过对于不能相交的直线和线段进行额外的判断来减少交点计算的运算量。但是考虑到判断的逻辑比较复杂、容易出错,我们最终还是采用了先算出所有交点,再舍弃非法点的方式。
e9c549ac0ba19321bd71b66bd903e35

独到之处:

  1. 在计算圆和圆的交点的过程中,我们将圆的一般公式输入MATLAB,并实现了MATLAB的计算流程。其好处在于能够保证程序的正确性和效率。
  2. 为了方便计算,我们储存了射线的方向。
  3. 为了降低计算量,我们存储了很多中间变量。
    • 如在圆相关的计算中,常常会用到圆半径的平方,我们便选择把半径的平方存为圆的参数;
    • 又如在判断点是否可行的过程中,涉及到很多比较操作,这些操作需要进行\(+-eps\)的操作,我们选择直接将参数\(+-eps\)存为参数,减少了运算耗费。
    • 又如圆的计算较为复杂,在观察了计算式后我们发现:运算过程中有很多重复的计算步骤,我们同样采取了存储中间计算结果的方式来减少这些运算步骤。

5. UML 类图

画出 UML 图显示计算模块部分各个实体之间的关系(画一个图即可)

6. 计算模块接口部分的性能改进

计算模块接口部分的性能改进 记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展示你程序中消耗最大的函数

从性能分析图中,可以看出,主要性能消耗在于使用vector存储交点之后,通过sort进行去重来得到交点个数。同时,经过多次测试,发现对于数据量的增加,这部分的性能消耗一直占据主要部分,那么我们应该从这部分入手进行优化测试,同时进行思考其他部分是否可以继续减少时间。

hash映射

考虑到主要占用时间是vector存储交点后去重的问题,那么一个很简单的想法就是不集体去重,而是每次插入直接判断是否重复,若重复则不插入,这里的查找复杂度为常数级,所以我们认为可能会减少这部分运行的时间。

但经过对比实验之后,发现如果数据量较大时,hash中的冲突维护需要更多的时间,那么就导致了速度越来越慢,复杂度退化为log级乃至线性级。经过统计,发现在大量数据时两者差距并不是很大。

Bentley-Ottmann算法

本次作业中,比上次增加了线段部分的计算,那么很自然的想到扫描线算法。

而在上次作业的博客中已经分析过本算法,本次将其搬运过来。

这个算法大致的思路是:

如果一个线段和另外一个线段有交点,那么用一条直线从上向下扫,可知有交点的线段一定是相邻的,如图所示:

img

可以看出,bc交点之上,bc线段相邻。而有bd线段相交但不相邻的情况,这种情况下,当直线扫过bc交点,达到bd交点上时,可以知道bd是相邻的,所以通过这样的算法,就可以维护两个数据结构,得到交点个数:一条链表,用来记录所有线段的端点和已经找到的交点,每个点按y的递减顺序存储,若y相同,则按x递增顺序存储;一棵二叉树,负责记录与扫描线相交的线段,每条线段按照上端点的x坐标递增顺序存储。

如此,可以通过扫描线得到交点个数,算法时间复杂度为\(O((n+k)logn)\),n为线段数量,k为焦点数量。

分组去重

由于使用sort并去重,其时间复杂度是\(O(nlogn)\),那么经过思考,可以通过线线分组,线圆分组等,进行局部去重之后再整体去重。

后来发现,这种做法在分组内部交点重复较多的时候会比较好用,但事实上数据不会像设定的这样,而且书写起来不美观,所以暂时放弃了这种做法。

结构体和类

本次代码使用了类进行几何图形的存储,实现了比较好的封装,同样的,使用结构体也是可以进行比较好的封装的。

但这两者的一个问题就是,封装的比较好,必然带来了速度的下降,所以如果使用数组分散存储,会带来速度的提升这点是必然的,但是因为会导致数据维护难度下降、模块耦合度增高等负面影响,所以暂时放弃了这种做法。

7.Design by Contract,Code Contract

Design by Contract,Code Contract 的内容,描述这些做法的优缺点,说明你是如何把它们融入结对作业中的

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. These specifications are referred to as "contracts", in accordance with a conceptual metaphor with the conditions and obligations of business contracts.

---- From WikiPedia_Design by contract

即契约式编程(DbC)需要程序给出并遵守三种条件:

  • 先决条件
    • 执行代码前必须要成立的条件;
    • 对函数参数和调用函数时的上下文的约束。
  • 后置条件
    • 代码执行后必须要成立的条件;
    • 函数的返回值和退出函数时的上下文的约束。
  • 不变条件
    • 在程序运行过程中,始终成立的条件;

契约编程的优点在于,更加严谨地描述程序的功能,也能帮助他人理解程序的作用。明确功能定义,保证程序的正确性。当然它也有一些缺点,如代码可能较为臃肿、制约运行效率;而契约的撰写成本也相对较高。

在本次结对编程的过程中,我们对错误处理程序定义了契约,如规定了错误处理模块执行前后的影响。又如在编写UI和计算模块时,我们定义好了数据流协议,也保证了模块间配合的正确性。

在编码过程中,对于每个方法、类,我们都提前设计好作用、参数、返回值、期望的异常等。这不仅方便了我们编码、提高了程序的正确性,也为编码结束后进行单元测试提供了方便。因此,在开始编码前进行设计和契约是有效的提升编程效率的方法。

8.计算模块部分单元测试展示

计算模块部分单元测试展示 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到 90% 以上,否则单元测试部分视作无效

单元测试的思路:

  • 单元测试的目的是进行代码覆盖性检查,以防止代码功能性错误。
  • 我们在单元测试中主要考虑了以下情况:
    • 不同几何图形之间的交点:四种几何图形,两两相交,共有12种情况;
    • 不同几何图像之间的位置情况:
      • 包括线之间的相交、相离、重合等情况;
      • 圆之间的相交、相切、相离等;
      • 线段、射线中存在的“共线,但有一点相交”的情况;
    • 错误处理:对于重合的圆、有无穷交点的线,我们需要返回错误类型;
    • 精度问题:由于我们使用的是double类型,在等于等比较判断时,需要考虑eps的影响。精度问题也是我们需要在单元测试中考虑到的。

消除 Code Quality Analysis 中的所有警告:

9.计算模块部分异常处理说明

计算模块部分异常处理说明 在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景

按照程序设计的思路,我们将需要处理的错误分为如下几个:

enum class ErrorType {
	NoError, CommandNum, CommandFormat, FileNotExist,
	EmptyFile, NumOutOfRange, AssertNum,
	FigureOutOfRange, LinePointOverlap, FigureFormat, SameLine, SameCircle,
};

命令行输入参数过少

这一点是需要对输入的命令行指令进行检测,所以基于定义的枚举,设计了如下单元测试:

int argc = 4;
char* argv[5];
argv[1] = "-i";
argv[2] = "input.txt";
argv[3] = "-o";
argv[4] = "output.txt";
ErrorType errortype = CommandError(argc, argv);
Assert::AreEqual(int(errortype), int(ErrorType::CommandNum));

命令行参数格式不对

同样的,这时命令行的数量正确,但是所输入的指令可能不符合本次作业的要求,设计了如下单元测试:

int argc = 5;
char* argv[5];
argv[1] = "";
argv[2] = "input.txt";
argv[3] = "";
argv[4] = "output.txt";
ErrorType errortype = CommandError(argc, argv);
Assert::AreEqual(int(errortype), int(ErrorType::CommandFormat));

输入文件不存在

虽然命令行的参数可能是对的,但是所需要输入的文件如果在当前路径下不存在的话,同样会导致程序错误,设计了如下单元测试:

int argc = 5;
char* argv[5];
argv[1] = "-i";
argv[2] = "notexistfile.txt";
argv[3] = "-o";
argv[4] = "output.txt";
ErrorType errortype = CommandError(argc, argv);
Assert::AreEqual(int(errortype), int(ErrorType::FileNotExist));

输入文件为空

如果输入文件存在,但是文件为空,那么有可能导致程序无法接收到任何信息而无限卡在输入阶段,设计了如下单元测试:

std::ifstream file;
file.open("empty.txt");
ErrorType errortype = EmptyFileError(file);
Assert::AreEqual(int(errortype), int(ErrorType::EmptyFile));

输入n不为数字

对于输入部分的检测全部结束,那么接下来该对其输入的内容进行检测了,这里需要判断读入的n是否是一个数字,设计了如下单元测试:

std::string n = "s";
ErrorType errortype = NumError(n);
Assert::AreEqual(int(errortype), int(ErrorType::AssertNum));

输入n超过范围

根据题目中定义可知,n的范围为[1, 500000],这样,就需要检测n超过边界的情况了,设计了如下单元测试:

std::string n = "-1";
ErrorType errortype = NumError(n);
Assert::AreEqual(int(errortype), int(ErrorType::NumOutOfRange));

输入图形格式错误

对于本题目来说,线类的图形需要L/S/R x1 y1 x2 y2这样的格式进行输入,圆类的图形需要C x y r这样的格式进行输入,所以需要对图形输入的格式进行相关的规范化,设计了如下单元测试:

std::string figure = "";
CorrectFigure correctfigure = FigureError(figure);
Assert::AreEqual(int(correctfigure.errortype), int(ErrorType::FigureFormat));

输入图形参数超过范围

对于本题目来说,除了圆半径之外的参数范围为[-100000, 100000],圆半径的范围为[1, 100000],那么就势必要进行数字是否超过范围的判断,设计了如下单元测试:

long long n = 5;
int low = 1;
int high = 2;
ErrorType errortype = BondError(n, low, high);
Assert::AreEqual(int(errortype), int(ErrorType::FigureOutOfRange));

输入的直线两点重合

对于本题目来说,直线需要两个不同的点去构造,所以两个端点不可以重合,设计了如下单元测试:

long long a = 1;
long long b = 1;
long long c = 1;
long long d = 1;
ErrorType errortype = OverlapError(a, b, c, d);
Assert::AreEqual(int(errortype), int(ErrorType::LinePointOverlap));

输入直线重合

vector<struct Position> res;
Line line1(1, 1, 1, 0);
Line line2(1, 2, 1, 3);
ErrorType err = line2.lineIntersect(line1, res);
Assert::IsTrue(err == ErrorType::SameLine);

输入圆重合

vector<struct Position> res;
Circle c1(0, 0, 5);
Circle c2(0, 0, 5);
ErrorType err = c1.circleIntersect(c2, res);
Assert::IsTrue(err == ErrorType::SameCircle);

对于如上所有单元测试部分,均进行了反向测试,即对其命令的正确形式也进行了相关测试,以保证代码覆盖率。

10. 界面模块的详细设计过程

界面模块的详细设计过程在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程

使用了qt creator进行界面的编写,界面如下:

其中包含了一个Widget,两个ListWidget,两个Linetext,四个Push Button。

Widget

这个是画图界面,我们采用的是QCustomerPlot这个库,所以需要在工程的ui界面拖动处将其“提升”:

这样原本使用QCustomerPlot库的时候,replot之后不需要再show,就可以出现所画图形,也不会另外弹出画图窗口。

图形绘制

我们实现了draw_line和draw_cycle两个函数,用来画出线类和圆类图形。

对于draw_line,由于QCustomerPlot并不提供直线的绘制功能,而只提供线段绘制,故我们采用将给出两点坐标延拓到画布边界的方式来表示直线。

对于draw_cycle,由于QCustomerPlot并不提供圆形的绘制功能,故我们采用上下两圆弧拼接为一个圆的方式作为圆的绘制,需要注意的是,在绘制曲线时,要设置好点与点之间的间隔距离,太大会导致生成直线,太小会导致点过于多画的很慢。

同时,为了便于查看,将不同图形设置为不同的颜色。

界面移动、缩放

对于widget类,可以对其机型相关参数设置,以达到画图界面的移动、缩放等功能,部分代码如下:

ui->widget->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom| QCP::iSelectAxes |
                              QCP::iSelectLegend | QCP::iSelectPlottables);

交点绘制

为了便于查看,我们将交点用半径为0.02的圆形来绘制,部分代码如下:

for (int i = 0; i < int(positions.size()); i += 2) {
	line = QString("%1 %2").arg(positions[i]).arg(positions[i + 1]);
	draw_cycle(positions[i], positions[i + 1], 0.02, 100);
	ui->listwidget2->addItem(line);
}

其中draw_cycle参数的100表示曲线两点间包含圆直径*100个点,从而可以画出比较圆滑的曲线。

坐标显示

考虑到如果对于大量图形,保持坐标显示在屏幕上不是一个好思路,会遮挡几何图形,所以采用鼠标点击触发点坐标显示的函数,部分代码如下:

if (event->button() != Qt::LeftButton){
    return;
}
QPointF ChickedPoint = event->pos();
if(!ui->widget->viewport().contains(event->pos())){
    return;
}
double currentx = ui->widget->xAxis->pixelToCoord(ChickedPoint.x());
double currenty = ui->widget->yAxis->pixelToCoord(ChickedPoint.y());

对于每一次点击,进行绘图界面的刷新工作,显示出以两条相互垂直直线形成的标记,且在标记旁边出现此点坐标。

ListWidget

这部分需要使用两个模板,一个用来显示几何图形的相关参数,另一个用来显示交点数量以及坐标。

这里需要注意的就是,在每次删除或者增加图形的时候,要随之维护两者所承载的文本。

Linetext

这部分同样需要两个模板,一个用来显示所选用文件的路径,另一个 用来显示输入几何图形的参数。

Push Button

这部分需要使用四个模板,包括路径,添加文件,添加图形和删除图形。

需要说明的一点是,需要将这四个按钮全部固定为slot,才能在函数中进行调用:

路径

这部分代码需要负责,点击之后弹出路径选择模块,从本地中选择适合的文件路径进行输入,这一步是之后文件输入的前置条件,部分代码如下:

QString file_path = QFileDialog::getOpenFileName(NULL,"标题",".","*.txt");
if(file_path.isEmpty())
{
	return;
}
else {
	ui->text1->setText(file_path);
}

添加文件

添加文件一个按钮所需要做的事情包括,打开路径文本中的文件,对其中的图形参数进行处理,画图几个部分。最初设计时考虑添加一个画图按钮,在添加文件或者图形之后点击即可画图,后来经过思考,发现这样做的话可能会因为遗忘等原因忘记自己添加到哪个图形这样的窘境,所以将其与添加模块合并,下同。

部分代码如下:

QString path = ui->text1->text();
QFile file(path);
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
QString line = in.readLine();
int n = line.toInt();
for (int i = 0; i < n; i++) {
	line = in.readLine();
	ui->listwidget1->addItem(line);
	QStringList strlist = line.split(" ");
	create_array(strlist);
}
updatelistwidget2();
draw();

添加图形

这部分需要获取在文本框中输入的图形参数,进而画出相关图像,操作顺序与添加文件相似,部分代码如下:

QString line = ui->text2->text();
ui->listwidget1->addItem(line);
QStringList strlist = line.split(" ");
create_array(strlist);
updatelistwidget2();
draw();
ui->widget->replot();

删除图形

这里我提供了多选删除的操作,所以在想删除大量图形的时候,只需要选择自己想删除的图形,就可以一次性全部删除,而不用一个一个删。部分代码如下:

ui->listwidget1->setSelectionMode(QAbstractItemView::MultiSelection);

删除之后需要对交点重复计算,也需要对剩余图形进行重新绘图,部分代码如下:

while(ui->listwidget1->selectedItems().size()>0)
{
	QListWidgetItem* sel = ui->listwidget1->selectedItems().at(0);
	if (sel)
	{
		int r = ui->listwidget1->row(sel);
		sel = ui->listwidget1->takeItem(r);
	}
}

11. 界面模块与计算模块的对接

界面模块与计算模块的对接。详细地描述 UI 模块的设计与两个模块的对接,并在博客中截图实现的功能

为了实现书中所提到的松耦合原则,我们约定GUI模块和计算模块的交互只包含数据流。

通过如下接口实现两个模块的对接:

class DLL_EXPORT DLL
{
public:
    DLL();
    void setFigures(std::vector<int>);
    void setMapRange();
    void reset();
    void update();
    std::vector<double> getFigures();
    std::vector<double> getIntersects();
};

使得GUI界面只需要关注自己的画图功能即可。

我们约定直线为1,射线为2,线段为3,圆为4,将其存入连表中,五个元素为一个图形,格式为,图形形状,x1,y1,x2,y2,由于圆只包含三个参数,所以将其最后一位补0来统一格式进行数据流的传递。

对于交点,只需要传递交点坐标即可。

如此便实现了GUI和计算模块的交互过程,并且完全不需要依赖两者内部所调用的模块。

下面进行功能展示:

添加文件

添加图形

删除图形

交点及坐标

12. 核心模块的松耦合

我们和@17373325&17373318的同学进行了核心模块交换松耦合测试。在核心模块部分我们使用的是基本数据类型的数据流,而他们使用的是自定结构体,其他行为基本一致。因此在进行少量的改动之后,我们的GUI便可以配合他们的核心模块一起使用了。

GUI测试

交换后的展示结果如下:

图为使用其他组同学的核心Dll模块的运行结果。(我们的GUI+他们的Dll)

命令行测试

同样我们稍作修改,除了以上的GUI模块实现了对接,我们进行了命令行测试,测试结果如下:

13.描述结对的过程

描述结对的过程,提供两人在讨论的结对图像资料(比如 Live Share 的截图)。关于如何远程进行结对参见作业最后的注意事项

我们尝试使用了LiveShare来进行远程代码开发。LiveShare能提供类似Overleaf的共同开发环境,有效提高编码效率。但是可能由于网络原因,延迟较高,在我们本次开发中使用较少。

在设计、编码过程中,我们主要使用了腾讯会议来进行相互交流。腾讯会议的投屏功能,能够方便地展现一方的开发环境,方便结对编程。

同时我们使用Github进行进度管理和版本控制。如我们使用issue功能来设置和分配待完成事项等。

在交换时发现的问题

问题最大的部分就是接口的不一致,同样问题,不同的人有不同的实现方法,而由于我们是在实现功能之后再进行的松耦合交换,不可避免的导致接口无法直接使用,所以只能通过重新封装接口来实现。而且在这里面,我们认为在接口中将数据封装为数据流,而非是依赖于其它结构体的形式更加合理,使耦合度进一步降低。

14.说明结对编程的优点和缺点

说明结对编程的优点和缺点。同时描述结对的每一个人的优点和缺点在哪里(要列出至少三个优点和一个缺点)

结对编程

定义为:结对编程是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。结对编程对开发程序有很多好处。比如增加纪律性,写出更好的代码等。一个人担任领航员的角色,另一个人担任导航员的角色。在本次的结对编程过程中,我和我的partner多次交换角色。我主要处理计算模块和测试,而我的partner主要进行错误处理模块和UI模块。

优点:

  • 代码复审:一个人写的代码两个人看,能够及时检查,减少bug数量。
  • 两个人是一个小Team,可以适时进行分工合作,提高工作效率。
  • 起到互相激励的作用: 一个人可能会拖延,但是两个人相互督促相互push,能让两个人的工作效率提升。同时由于两个人的角色可以互换,不至于因为重复干同样的工作而感到十分疲惫。
  • 可以促进两者中较弱的那一方水平的增长,水平较低的那一方会潜移默化地受到水平较高的那一方的影响,学到新的东西。

缺点:

  • 远程结对开发沟通成本较高;
  • 结对开发者的技术水平可能会有区别,导致任务失衡;
  • 两个人可能会聊天而导致分散注意力,效率低下。
姓名 优点 缺点
ljy 1. 结对过程中善于交流;2. 有耐心;3.善于学习 有时比较摸
dxy 1. 编码能力强,能解决出现的问题;2. 交流能力强; 3. 有责任心 有时会比较急躁

15. 实现完程序之后,在PSP表格记录下你在程序的各个模块上实际花费的时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 10 20
Development 开发
· Analysis · 需求分析 (包括学习新技术) 400 500
· Design Spec · 生成设计文档 100 100
· Design Review · 设计复审 (和同事审核设计文档) 50 60
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
· Design · 具体设计 100 120
· Coding · 具体编码 400 500
· Code Review · 代码复审 50 100
· Test · 测试(自我测试,修改代码,提交修改) 300 450+50=500
Reporting 报告
· Test Report · 测试报告 20 30
· Size Measurement · 计算工作量 5 5
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 30
合计 1485 1995

分析:
预计时间和实际使用时间相差500分钟左右,主要差距在于:

  • 由于远程结对,需要考虑除了工作以外所需要的时间,如设备环境调试等;
  • 由于结对二人都没有接触过QT,所以学习过程花费了额外的时间;
  • 由于此次涉及的情况较为复杂,所以测试花费了较多的计划外时间。

在编码和测试的过程中,也能体悟到严谨详细的项目设计对后续工作带来的提升作用。

posted @ 2020-03-24 15:34  Lebway  阅读(357)  评论(2编辑  收藏  举报