软工结对博客

软工结对博客

项目 内容
这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) (北京航空航天大学 - 计算机学院)
这个作业的要求在哪里 结对项目作业
我的教学班级 005
项目地址 链接地址

一、PSP2.1

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

二、接口设计

  • Information Hiding

这一块其实做得非常不好,没有特意屏蔽掉内部实现细节,比如circle类,line类当中的变量都是以public的形式暴露出来。

  • Loose Coupling
virtual void upgrade_points(set& points, Line& line);
virtual void upgrade_points(set& points, Circle& circle);

所有的item都有上述方法,该方法只需要传入已有点集和所需要求解的相交线(圆),实现了低耦合的效果。

  • Interface Design
DLL_API void LoadFromFile(ifstream& input, vector& lines, vector& rays,
	vector& segments, vector& circles);

DLL_API void Calculate_Points(set& points, vector& lines, vector& rays,
	vector& segments, vector& circles);

LoadFromFile只传入符合要求的字符串,录入信息保存在vector容器中。
Calculate_Points负责计算,并将计算结果保存在保存在vector容器中。

三、UML

软工结对博客_第1张图片

四、性能计算及改进

软工结对博客_第2张图片

本次作业沿用上次作业,复杂度还是O(n^2),没有太大改进。主要还是插入点时红黑树的维护占用了大量时间。

五、契约式编程的理解及应用

契约式编程要求我们在『前提条件』、『后继条件』和『不变量条件』进行契约的检查。类似的,例如检查参数,一旦参数不对,当即撕毁契约。契约所约束的,是『一个为了确保程序正常运行的条件』,一旦契约被损毁,只有一个原因,那就是程序出了Bug,一旦出现问题,应该有调用方来检查,确保调用的时候,必须是合法的。

通过契约式设计可以使程序结构更加清晰,扩展性高。可以优化程序设计,提高代码的可靠性,简化调试工作,适合团队开发。

不足之处在于契约式编程需要学习撰写良契约的思想和技术,且撰写成本较高(当然回报大于成本),需要大量实践。

在编写代码过程中,主要在处理异常时用到了契约式设计的思想。先罗列出各种异常,然后再依次实现。

六、单元测试

来自队友做的单元测试与异常处理,覆盖率如下

软工结对博客_第3张图片

软工结对博客_第4张图片

就代码而言谈谈我的理解:

  • getIntersectPoint测试考虑了圆与线的三种情况,线与线的两种情况
			//case 1:line & line 相交
			line_1=new Line(0, 1, 2, 3);
			line_2=new Line(0, 0, -1, 1);
			points=line_1->getIntersectPoint(*line_2);
			Assert::AreEqual((int)points.size(), 1);
			Assert::AreEqual(points[0]==Point(-0.5,0.5),true);

			//case 2:Line & line 平行
			line_1=new Line(0, 2, 0, -2);
			line_2=new Line(3, 4, 3,-7);
			points = line_1->getIntersectPoint(*line_2);
			Assert::AreEqual((int)points.size(), 0);

			//case 3:Line & circle 相切
			line_1 = new Line(0, 2, 0, -2);
			cir_2 = new Circle(1, 1, 1);
			points = line_1->getIntersectPoint(*cir_2);
			Assert::AreEqual((int)points.size(), 1);
			Assert::AreEqual(points[0] == Point(0, 1), true);
			
			//case 4:Line & circle 相交
			line_1 = new Line(-4, 1, 0, 1);
			cir_2 = new Circle(0, 1, 1);
			points = line_1->getIntersectPoint(*cir_2);
			Assert::AreEqual((int)points.size(), 2);
			Assert::AreEqual((points[0] == Point(-1, 1) || points[1] == Point(-1, 1)), true);
			Assert::AreEqual((points[0] == Point(1, 1) || points[1] == Point(1, 1)), true);

			//case 5:Line & circle 相离
			line_1 = new Line(0, 2, 0, -2);
			cir_2 = new Circle(2, 1, 1);
			points = line_1->getIntersectPoint(*cir_2);
			Assert::AreEqual((int)points.size(), 0);
  • isLawful的测试覆盖了线段和射线两点之间、延长线上、端点等位置
			Ray ray(0,1,0,-1);
			Segment seg(1, 2, 6, 7);
			Assert::AreEqual(ray.islawful(Point(0,-2)), true);
			Assert::AreEqual(ray.islawful(Point(0,1)), true);
			Assert::AreEqual(ray.islawful(Point(0,2)), false);

			Assert::AreEqual(seg.islawful(Point(3,4)), true);
			Assert::AreEqual(seg.islawful(Point(1,2)), true);
			Assert::AreEqual(seg.islawful(Point(6,7)), true);
			Assert::AreEqual(seg.islawful(Point(-1,0)), false);
  • 还有对异常处理的覆盖测试,包括类别码非法、数值超限、定义线段端点重合、线区间重合等

七、异常处理

  • 点的坐标值超限
                TEST_METHOD(excep9) {
			//数值超限
			ifstream fin;
			fin.open("excep9.txt");
			vector lines;
			vector rays;
			vector segs;
			vector cirs;
			bool flag = false;
			/* 1
				L - 100001 4 -1 4
			*/
			try {
				LoadFromFile(fin, lines, rays, segs, cirs);
			}
			catch (exception& e) {
				if ((string)e.what() == "value of point out of range(-100000,100000)") flag = true;
			}
			Assert::AreEqual(true, flag);
		}
  • 线段的两点相同
		TEST_METHOD(excep8) {
			//定义线的两个端点相同
			ifstream fin;
			fin.open("excep8.txt");
			vector lines;
			vector rays;
			vector segs;
			vector cirs;
			bool flag = false;
			/*2
				L -1 4 -1 4
				R 2 5 - 1 2*/
			try {
				LoadFromFile(fin, lines, rays, segs, cirs);
			}
			catch(exception& e){
				if ((string)e.what() == "Use same points to define line/ray/segment") flag = true;
			}
			Assert::AreEqual(true,flag);
		}
  • 线有重合部分
                TEST_METHOD(excep4) {
			//ray & seg 区间重合
			bool flag = false;
			Ray* r1 = new Ray(0, 0, 1, 1);
			Segment* s2=new Segment(-1,-1,1,1);
			try {
				exception_if_InfPoints(*r1, *s2);
			}
			catch (exception& e) {
				if ((string)e.what() == "Has infinite points") flag = true;
			}
			Assert::AreEqual(true, flag);
		}
  • n<=0
		TEST_METHOD(excep10) {
			//illegal n
			ifstream fin;
			fin.open("excep10.txt");
			vector lines;
			vector rays;
			vector segs;
			vector cirs;
			bool flag = false;
			/*  0
				L - 100 4 -1 4
			*/
			try {
				LoadFromFile(fin, lines, rays, segs, cirs);
			}
			catch (exception& e) {
				if ((string)e.what() == "illegal n!") flag = true;
			}
			Assert::AreEqual(true, flag);
		}
  • 其他非法类别码
		TEST_METHOD(excep12) {
			//类别码非法
			ifstream fin;
			fin.open("excep12.txt");
			vector lines;
			vector rays;
			vector segs;
			vector cirs;
			bool flag = false;
			try {
				LoadFromFile(fin, lines, rays, segs, cirs);
			}
			catch (exception& e) {
				if ((string)e.what() == "typecode should be upppercase!") flag = true;
			}
			Assert::AreEqual(true, flag);
		}
  • 圆的半径小于等于0
		TEST_METHOD(excep11) {
			//圆半径<=0
			ifstream fin;
			fin.open("excep11.txt");
			vector lines;
			vector rays;
			vector segs;
			vector cirs;
			bool flag = false;
			/*  1
				C - 100 4 0
			*/
			try {
				LoadFromFile(fin, lines, rays, segs, cirs);
			}
			catch (exception& e) {
				if ((string)e.what() == "radius of circle <=0") flag = true;
			}
			Assert::AreEqual(true, flag);
		}

八、界面设计

软工结对博客_第5张图片

  • 定义导入按键:
connect(ui.btn_loadfile, &QPushButton::clicked, [=]() {
		//获取当前路径
		//在当前路径打开弹窗选择文件
		//返回选取的文件的绝对路径
		//调用LoadFromFile更新本地的几何对象容器
		//更新UI中下拉栏已有的几何对象,建立映射
	});
  • 定义添加对象按键:
//添加几何对象,假设UI输入的几何对象与已有几何对象不重合(不查重)
	connect(ui.btn_add, &QPushButton::clicked, [=]() {
		//提取几何对象类别码
		QString str = ui.comboBox_type->currentText();
		//提取输入的四个或三个整数
		//检查输入合法性
		try {
			exception_if_outBorder(fir_num);
			exception_if_outBorder(sec_num);
			exception_if_outBorder(thi_num);
			exception_if_outBorder(four_num);
			if (str == QString("circle")) { exception_if_illegalRadius(thi_num); }
			else { exception_if_samePoints(fir_num, sec_num, thi_num, four_num); }
		}
		catch (exception& e) {
			QMessageBox::critical(this,"critical",e.what());
			return;
		}
		//创建对象,并更新UI中下拉栏已有的几何对象,建立映射
	});
  • 定义删除对象按键:
//删除几何对象
	connect(ui.btn_delete, &QPushButton::clicked, [=]() {
		//更新本地几何对象集合
		QString cur_str = ui.comboBox_delete->currentText();
		this->delete_obj(cur_str);//删除容器中的几何对象
		//删除映射
		int cur_index = ui.comboBox_delete->currentIndex();
		ui.comboBox_delete->removeItem(cur_index);

		//重置points
		points.clear();

		//调用计算模块,更新points
		try {
			Calculate_Points(points, lines, rays, segments, circles);
		}
		catch (exception& e) {
			QMessageBox::critical(this, "critical", e.what());
		}
	});
  • 定义绘制按键:
connect(ui.btn_paint, &QPushButton::clicked, [=]() {
		update();//重新触发PaintEvent
		//更新UI显示的交点数
		QString str;
		str.setNum(points.size());
		ui.lineEdit_ans->setText(str);
	});
  • 定义画图事件PaintEvent
QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing);//抗锯齿,美化图形
	painter.translate(500, 500);//更改坐标原点
	//定义画布区域 y>=280,O坐标(500,500)
	//绘制坐标轴 
	painter.drawLine(QLineF(QPoint(-500, 0), QPoint(500, 0)));
	painter.drawLine(QLineF(QPoint(0, 500), QPoint(0, -220)));

	//比例尺20:1
	//绘制几何对象
	
	//设置画笔颜色和宽度
	QPen pen((QColor)Qt::darkCyan);
	pen.setWidth(3);
	painter.setPen(pen);
	
	//绘制直线
	for (auto& line : lines) {
		double A = line.A, B = line.B, C = line.C;
		double x1, y1 = 11, x2, y2 = -15;
		if (A == 0) {
			y1 = y2 = -C / B;
		}
		else {
			x1 = (-C - B * y1) / A;
			x2 = (-C - B * y2) / A;
		}
		painter.drawLine(QPoint(x1 * 20, -y1 * 20), QPoint(x2 * 20, -y2 * 20));
		//y=11;x1
		//y=-15,x2;
	}
	//同理,遍历rays和segments,计算画布边界的相关点,然后绘制
	//遍历points,画点

九、模块对接

DLL_API void LoadFromFile(ifstream& input, vector& lines, vector& rays,
	vector& segments, vector& circles);

DLL_API void Calculate_Points(set& points, vector& lines, vector& rays,
	vector& segments, vector& circles);

将计算模块导出为dll,向UI模块提供LoadFromFile和Calculate_Points接口;

LoadFromFile函数需传入文件流的引用和各几何对象容器的引用;

Calculate_Points函数传入各几何对象容器的引用和交点集的引用

十、结对过程

​ 基本上是队友扛着我往前跑,第一次交流之前就基本完成了异常处理和单元测试,以及UI的部分工作。由于剩下的工作量比较少,我比较偏执地试图用QGraphicview完成图像的绘制,但是遇到很多很多困难,队友在关键时刻给了我很多帮助,及时地共享资料(虽然最后来不及实现),培养了我与人交流的能力。

软工结对博客_第6张图片 软工结对博客_第7张图片

结对编程优缺点

对象 优点 缺点
结对编程 1、效率提高 ; 2、更容易发现bug ; 3、可以从队友身上学到很多; 4、代码的交流有助于提高代码能力 1、相互不太了解,磨合成本高;2、初期对队友的代码很难理解;3、可能会排斥队友的代码风格
1、细致严谨;2、做事前准备充分;3、追求完美 动手慢,一直在找资料,做练习题
队友 1、码力十足;2、动手快;3、勤于交流;4、善于鼓励;5、对bug反应灵敏 函数封装随意,public泛滥,接口高耦合

你可能感兴趣的:(软工结对博客)