• 软工结对博客


    软工结对博客

    项目 内容
    这个作业属于哪个课程 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<Point>& points, Line& line);
    virtual void upgrade_points(set<Point>& points, Circle& circle);
    

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

    • Interface Design
    DLL_API void LoadFromFile(ifstream& input, vector<Line>& lines, vector<Ray>& rays,
    	vector<Segment>& segments, vector<Circle>& circles);
    
    DLL_API void Calculate_Points(set<Point>& points, vector<Line>& lines, vector<Ray>& rays,
    	vector<Segment>& segments, vector<Circle>& circles);
    
    

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

    三、UML

    四、性能计算及改进

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

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

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

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

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

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

    六、单元测试

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

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

    • 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<Line> lines;
    			vector<Ray> rays;
    			vector<Segment> segs;
    			vector<Circle> 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<Line> lines;
    			vector<Ray> rays;
    			vector<Segment> segs;
    			vector<Circle> 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<Line> lines;
    			vector<Ray> rays;
    			vector<Segment> segs;
    			vector<Circle> 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<Line> lines;
    			vector<Ray> rays;
    			vector<Segment> segs;
    			vector<Circle> 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<Line> lines;
    			vector<Ray> rays;
    			vector<Segment> segs;
    			vector<Circle> 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);
    		}
    

    八、界面设计

    • 定义导入按键:
    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<Line>& lines, vector<Ray>& rays,
    	vector<Segment>& segments, vector<Circle>& circles);
    
    DLL_API void Calculate_Points(set<Point>& points, vector<Line>& lines, vector<Ray>& rays,
    	vector<Segment>& segments, vector<Circle>& circles);
    

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

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

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

    十、结对过程

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

    结对编程优缺点

    对象 优点 缺点
    结对编程 1、效率提高 ; 2、更容易发现bug ; 3、可以从队友身上学到很多; 4、代码的交流有助于提高代码能力 1、相互不太了解,磨合成本高;2、初期对队友的代码很难理解;3、可能会排斥队友的代码风格
    1、细致严谨;2、做事前准备充分;3、追求完美 动手慢,一直在找资料,做练习题
    队友 1、码力十足;2、动手快;3、勤于交流;4、善于鼓励;5、对bug反应灵敏 函数封装随意,public泛滥,接口高耦合
  • 相关阅读:
    树链剖分
    NOI2015 软件包管理器(树链剖分+线段树)
    9.11NOIP模拟题
    9.10NOIP模拟题
    bzoj1121[POI2008]激光发射器SZK(结论)
    9.9 NOIP模拟题
    树形dp
    P1272 重建道路(树形dp)
    Codeforces Round #419 (Div. 2)
    9.2NOIP模拟题
  • 原文地址:https://www.cnblogs.com/buaayzx/p/12560627.html
Copyright © 2020-2023  润新知