软件工程结对项目

软件工程结对博客

1、教学班级和github地址

项目 内容
这个做业属于哪一个课程 2020计算机学院软件工程(罗杰 任健)
这个做业的要求在哪里 结队项目做业
教学班级 006
项目地址 结队项目做业

2、在开始实现程序以前,在下述 PSP 表格记录下你估计将在程序的各个模块的开发上耗费的时间。(0.5')

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

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

Information Hiding:

信息隐藏的设计意图为将数据进行封装,不让外部访问,保证安全。本次做业将不一样的数据结构封装成为不一样的类,可是对于交点的求解未封装进类中而是采用了全局函数的形式,具体细节在后文介绍。html

Interface Design:

不一样的类只暴露相应的接口,接口的设计是为了让可以很好的铆接程序的各个部分,所谓的面向接口编程就是只关注接口的参数和返回值,至于接口内部实现当作黑盒并不关心。咱们本次结对做业的主要接口为5个全局变量,前端与后端的主要交互手段为5个存储着交点、直线、线段、射线、圆的容器,前端负责绘图,后端负责计算,以此达到一个较好的铆接。原本想利用重载,可是后来又从新编写了不一样的函数名,见名知义,对传入参数的不一样,使用不一样的计算函数。具体设计会在后文介绍。前端

Loose Coupling:

咱们本次做业采用了先后端分离的模式,后端负责计算数据,前端负责展现,可是因为未和其余队伍造成一个良好的接口匹配,因此没法向外部体现这一点。可是在结对过程当中咱们是分工明确的,后端编写者不知道前端编写者具体的逻辑,只知道提供接口便可,前端编写者亦然。node

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

本次做业一共使用了6个类,其中1个类为UI类,剩下5个类为图形类,分别为点、直线、射线、线段、圆。git

函数一共分为5大类,函数的交互方法以下图(箭头为调用关系):
github

算法关键:算法

咱们算法的关键在于,计算交点的方法时间复杂度为:\(O(n^2)\),利用map并重载运算符保证点的惟一性,最后使用先后端分离的方法,前端接受请求,后端处理请求并返回给前端相应数据最终实现需求。编程

5、阅读有关 UML 的内容:https://en.wikipedia.org/wiki/Unified_Modeling_Language。画出 UML 图显示计算模块部分各个实体之间的关系(画一个图便可)。(2’)

实体关系:
后端

其中带有箭头之间的实体表明具备交互,如图所示,前端想要获取后端数据必须经过后端提供的接口。安全

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

在2000条测试数据下:read_file()函数为总的计算花费,而originGeo()函数和show()函数分别是图形的添加和图形的绘制,本次做业的性能瓶颈为图像的绘制,由于采用第三方库的缘故,因此很难进行更进一步的优化。
微信

能够看到图像绘制占用了整体CPU时间的31.8%

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

按合同编程的优势:

  • 有较强的规则,不易出现工做重叠或者接口不匹配的状况
  • 可以在开发之初就计算出工做的大体时间,以及工做内容的复杂度

按合同编程的缺点:

  • 容易限制思惟,按照固定的方式进行开发容易被局限
  • 有些小工程也许构建合同就显得性价比不高

本次编程虽然没有将合同文本化,可是仍是作了较多的交流好比固定接口,如下是微信交流确认接口的界面

后来的接口交流补充文件截图:

8、计算模块部分单元测试展现。展现出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试获得的测试覆盖率截图,发表在博客中。要求整体覆盖率到 90% 以上,不然单元测试部分视做无效。(6')

测试数据为2000条直线,测试结果以下:

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

异常处理模块咱们也采用了先后端分离的模式,分为几种情形:

  • 当第一次文件读入存在错误时,不绘制UI界面,直接结束程序

输入文件为

效果如图所示:

  • 当从UI界面提交文件格式错误时,输出“wrong input”,

输入样例同上

输出为:

  • 当输入实际图形少于给出的数据总数时,测试以下:

输入数据:

输出结果:

  • 当输入文件找不到时,报错以下:

输出结果:

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

咱们的界面设计使用了QT库,仍是用了QCustomplot这样一个第三方库,部分代码借鉴了Qcustomplot的样例模板,界面布局以下图:

大部分功能使用了按键与槽函数的结合,代码举例以下:

//可拖动
customPlot->setInteraction(QCP::iRangeDrag, true);

//可缩放
customPlot->setInteraction(QCP::iRangeZoom, true);

//曲线可选
customPlot->setInteraction(QCP::iSelectPlottables, true);

//曲线 ctrl 多选
customPlot->setInteraction(QCP::iMultiSelect, true);

//坐标轴可选
customPlot->setInteraction(QCP::iSelectAxes, true);
//图例可选
customPlot->setInteraction(QCP::iSelectLegend, true);
// 选中轴时上下轴一块儿被选中
connect(customPlot, SIGNAL(selectionChangedByUser()), this,SLOT(selectionChanged()));
// 选中轴时,鼠标拖动智能移动一边的轴,滚轮也是同样
connect(customPlot, SIGNAL(mousePress(QMouseEvent*)), this,SLOT(mousePress()));
connect(customPlot, SIGNAL(mouseWheel(QWheelEvent*)), this,SLOT(mouseWheel()));

//多选框切换时,直线输入切换成圆输入
connect(ui->comboBox,SIGNAL(activated(int)),this,SLOT(switchGeo()));

connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(addGeo()));

//文件输入
connect(ui->pushButton_2, SIGNAL(clicked()), this,SLOT(fileInput()));

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

前端后端对接使用了5个容器:

map <node, int>* nodes;
vector<line>* lines;
vector<rays>* rayss;
vector<lise>* lises;
vector<Cycle>* cycles;

5个指针指向后端的全局变量,获得计算出的交点、直线、圆的全部信息,绘制图形的代码以下:

void MainWindow::originGeo() {
	
	//画交点
	QVector<double> nodex;
	QVector<double> nodey;
	for (map <node, int>::iterator iter = (*nodes).begin(); iter != (*nodes).end(); iter++) {
		node temp = iter->first;
		double x = temp.getX();
		double y = temp.getY();
		nodex.push_back(x);
		nodey.push_back(y);
	}
	QCPGraph* graph = customPlot->addGraph();
	graph->setData(nodex, nodey);
	graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ScatterShape::ssDisc, 5));
	QPen graphPen;
	graphPen.setColor(QColor(255,255,255));
	graphPen.setWidthF(rand() / (double)RAND_MAX * 2 + 1);
	graph->setPen(graphPen);
	graph->setName("交点集合");

	int cnt = (*nodes).size();
	ui->lineEdit_8->setText(QString::number(cnt));

	if (cnt >= 20) {
		customPlot->legend->setVisible(true);
	}

	//----------------------------------------
	//画直线
	int linesize = (*lines).size();
	for (int i = 0; i < linesize; i++) {
		line l = (*lines)[i];
		int x1 = l.getNode1().getX();
		int y1 = l.getNode1().getY();
		int x2 = l.getNode2().getX();
		int y2 = l.getNode2().getY();
		if (l.getExitK()) {
			double k = l.getK();
			double b = (-1) * l.getC() / l.getB();
			for (int i = 0; i < 10000; i++) {
				(*valueY)[i] = k * (*indexX)[i] + b;
			}
			QCPGraph* graph = customPlot->addGraph();
			graph->setData(*indexX, *valueY);
		}
		else {
			//斜率不存在时不支持画图
			continue;
		}
		QPen graphPen;
		graphPen.setColor(QColor(rand() % 245 + 10, rand() % 245 + 10, rand() % 245 + 10));
		graphPen.setWidthF(rand() / (double)RAND_MAX * 2 + 1);
		customPlot->graph()->setPen(graphPen);
		//customPlot->graph()->setName("line(编号:" + QString::number(customPlot->graphCount()) + ")");
		const QString name = "L " + QString::number(x1) + " " + QString::number(y1) + " " + QString::number(x2) + " " + QString::number(y2);
		customPlot->graph()->setName(name);
	}

	//----------------------------------------
	//画射线
	int raysize = (*rayss).size();
	for (int i = 0; i < raysize; i++) {
		rays ray = (*rayss)[i];
		int x1 = ray.getStart().getX();
		int y1 = ray.getStart().getY();
		int x2 = ray.getN().getX();
		int y2 = ray.getN().getY();
		if (ray.getExitK()) {
			double k = ray.getK();
			double b = (-1) * ray.getC() / ray.getB();
			QVector<double> fx;
			QVector<double> fy;
			for (int i = 0; i < 10000; i++) {
				(*valueY)[i] = k * (*indexX)[i] + b;
				node tempnode((*indexX)[i], (*valueY)[i]);
				if (ray.judge(tempnode)) {
					fx.push_back((*indexX)[i]);
					fy.push_back((*valueY)[i]);
				}
			}
			QCPGraph* graph = customPlot->addGraph();
			graph->setData(fx, fy);
		}
		else {
			//斜率不存在时不支持画图
			continue;
		}
		QPen graphPen;
		graphPen.setColor(QColor(rand() % 245 + 10, rand() % 245 + 10, rand() % 245 + 10));
		graphPen.setWidthF(rand() / (double)RAND_MAX * 2 + 1);
		customPlot->graph()->setPen(graphPen);
		const QString name = "R " + QString::number(x1) + " " + QString::number(y1) + " " + QString::number(x2) + " " + QString::number(y2);
		//customPlot->graph()->setName("ray(编号:" + QString::number(customPlot->graphCount()) + ")");
		customPlot->graph()->setName(name);
	}

	//----------------------------------------
	//画线段
	int lisesize = (*lises).size();
	for (int i = 0; i < lisesize; i++) {
		lise ls = (*lises)[i];
		int x1 = ls.getNode1().getX();
		int y1 = ls.getNode1().getY();
		int x2 = ls.getNode2().getX();
		int y2 = ls.getNode2().getY();
		if (ls.getExitK()) {
			double k = ls.getK();
			double b = (-1) * ls.getC() / ls.getB();
			QVector<double> fx;
			QVector<double> fy;
			for (int i = 0; i < 10000; i++) {
				(*valueY)[i] = k * (*indexX)[i] + b;
				node tempnode((*indexX)[i], (*valueY)[i]);
				if (ls.judge(tempnode)) {
					fx.push_back((*indexX)[i]);
					fy.push_back((*valueY)[i]);
				}
			}
			QCPGraph* graph = customPlot->addGraph();
			graph->setData(fx, fy);
		}
		else {
			//斜率不存在时不支持画图
			continue;
		}
		QPen graphPen;
		graphPen.setColor(QColor(rand() % 245 + 10, rand() % 245 + 10, rand() % 245 + 10));
		graphPen.setWidthF(rand() / (double)RAND_MAX * 2 + 1);
		customPlot->graph()->setPen(graphPen);
		const QString name = "S " + QString::number(x1) + " " + QString::number(y1) + " " + QString::number(x2) + " " + QString::number(y2);
		customPlot->graph()->setName(name);
		
	}

	//----------------------------------------
	//画圆
	int circlesize = (*cycles).size();
	valueY1 = new QVector<double>(10000);
	for (int i = 0; i < circlesize; i++) {
		Cycle c = (*cycles)[i];
		int x = c.getC().getX();
		int y = c.getC().getY();
		int r = c.getR();
		const QString name = "C " + QString::number(x) + " " + QString::number(y) + " " + QString::number(r);
		r = r * r;
		double temp = 0;
		double temp1 = 0;
		for (int i = 0; i < 10000; i++) {
			temp = ((*indexX)[i] - x) * ((*indexX)[i] - x);
			temp1 = sqrt(r - temp);
			(*valueY)[i] = y + temp1;
			(*valueY1)[i] = y - temp1;
		}
		//int cnt = customPlot->graphCount();


		customPlot->addGraph();
		customPlot->graph()->setData(*indexX, *valueY);
		customPlot->graph()->setName(name);
		customPlot->addGraph();
		customPlot->graph()->setData(*indexX, *valueY1);
		customPlot->graph()->setName(name);
	}
	
	customPlot->replot();
	return;
}

功能包括:

  • 能够在图像上直接选择图形进行删除,删除两条直线后以下图(交点个数随之变化):

  • 右侧栏能够添加直线,并更新交点,操做以下图:

  • 右侧栏能够读取文件,文件内容在白色框中显示,以下图:

  • 图像点击后提示点击所在的位置坐标,以下图

  • 图像能够进行伸缩和拉伸,单机轴能够进行拖动,双击轴能够进行修改轴名

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

结对过程当中咱们进行了详细的分工,黎正宇负责后端逻辑,我负责前端UI设计。商量好接口后咱们进行了语音而后一块儿编程。

十3、看教科书和其它参考书,网站中关于结对编程的章节,例如:http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html ,说明结对编程的优势和缺点。同时描述结对的每个人的优势和缺点在哪里(要列出至少三个优势和一个缺点)。(5')

结对编程的优势:

  • 能够很好的分工协做,两我的分工并深刻本身的一小部分能够很大的提升效率
  • 两我的思惟的碰撞能够产生出更好的设计
  • 结对编程能够很好的锻炼沟通协做能力
  • 两我的互相学习,分别提升了我的的编程能力

结对编程的缺点:

  • 两我的作同一件事,沟通很差的状况下容易冲突
  • 两我的容易工做量分配不均,能力有高有低,工程付出程度不一样。
相关文章
相关标签/搜索