结对项目作业

1. 项目地址

项目 内容
课程 软件工程
作业 结对项目作业
教学班级 005
项目地址 https://github.com/huangjihui511/IntersectProject2

2. PSP 表格

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

3. 接口设计

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

信息隐蔽是开发整体程序结构时使用的法则,即将每个程序的成分隐蔽或封装在一个单一的设计模块中,定义每一个模块时尽可能少地显露其内部的处理。信息隐蔽原则对提高软件的可修改性、可测试性和可移植性都有重要的作用。

以下列举了一些信息隐藏原则的应用。
1 多层设计中的层与层之间加入接口层;
2 所有类与类之间都通过接口类访问;
3 类的所有数据成员都是private,所有访问都是通过访问函数实现的;

藕合度是度量一个代码单元在使用时与其他单元的关系。松耦合是一个单元无需其他代码单元特别的配合而可以使用。

我们运用了单例模式的思想封装出一个core类,相当于计算处理器,用vector存储几何体,拥有计算交点的方法。这样后端就被完整封装,和ui之间的耦合很松散,只需要在ui中创建core对象。

我们写了一个Geometry结构体用来存储直线和圆的实例(为了在core中形成一个几何体容器,不区分直线和圆的具体类别),使用void getObj(Circle& obj)或void getObj(Line& obj)的方法根据传入参数的类型得到对应实例,体现了信息的隐藏。

但是我们Line,Circle类的属性是public的,这一点不够谨慎,没有充分隐藏信息。

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

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

Point类:继承自pair,表示坐标点。

Line类:表示直线、射线、线段,类别由属性type标识。包含计算直线交点的函数getIntersection_ll。

class Line {
public:
	double a;
	double b;
	double c;
	GType type;
	Point e;	//方向向量
	Point p1;	//输入点
	Point p2;

	Line();
	Line(Point source, Point target, GType type);
	int getIntersection_ll(set<Point>* intersections, Line l1, Line l2);
	void operator=(const Line& line);
};

Circle类:表示圆。包含计算两圆交点的函数getIntersection_cc。

class Circle {
public:
	Point c;
	double r;

	Circle();
	Circle(Point c, double r);
	void operator=(const Circle& circle);
	int getIntersection_cc(set<Point>* intersections, Circle c1, Circle c2);
};

Geometry结构体:包含一个Line或Circle类的实体,和一个指示类别的Gflag(取值为L或C)。这样就可以将Line和Circle的对象加入一个vector

struct Geometry {
	GType Gflag;
	union {
		Line lObj;
		Circle cObj;
	};

	Geometry(Line l);
	Geometry(Circle c);
	void getObj(Line& obj);
	void getObj(Circle& obj);
	void operator=(const Geometry& g);
};

举例,将Line加入vector

vector<Geometry> geomrties;
Line *l;
geomrties.push_back(*l);

Core类:封装的计算核心模块,方便测试。

class DLL3_API Core {
public:
	set<Point> intersections;			//交点集合
	vector<Geometry> geomrties;			//几何体
	vector<string> errorInformations;	//错误信息
	int isValid = 1;
	void addGeomrties(ifstream *fin);	//从文件增加几何体
	void addGeomrtie(string text);		//增加单个几何体
	int intersect();					//求geomrties内几何体的交点
	int addError(string input);			//增加错误信息
};

函数与类的关系:

关键方法基本都封装在了类里面。先调用core.addGeomrties()函数增加几何体,再调用core.intersect()计算交点。addGeomrties函数是通过遍历文件调用若干addGeomrtie实现的,在测试阶段可以这样调用addGeomrtie("L 0 0 1 1")直接增加输入。intersect函数计算每两个几何体之间的交点,根据几何体类别分别调用getIntersection_ll、getIntersection_cc、getIntersection_cl。

算法关键:

圆与直线的交点算法与作业一相同,不再赘述。为了实现线段和射线的扩展,只需要把他们当作直线计算交点,然后判断交点是否在线段或者直线上。由于交点一定在线段或者射线延伸出来的直线上,只需判断交点的横坐标是否在线段端点之间,或者射线无限延伸的那一边。

判断交点与直线位置关系

错误处理中判断“无限交点”这一项,判断直线类的几何体是否重合条件比较复杂。普通直线的重合判断公式是\(\frac{A1}{A2}=\frac{B1}{B2}=\frac{C1}{C2}\)。一种特殊情况是线段、射线所在直线重合时,由于自身长度有限可能并不重合。

5. UML图

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

6. 性能改进

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

我们采用了\(O(n^2)\)的交点求解算法。虽然求线段交点可以采用Bentley & Ottmann提出的基于扫描线的算法,将复杂度降到\(O(nlogn)\),但是我们的问题中有多种几何体,只优化线段与线段效果有些鸡肋。

消耗最大的函数是getIntersection_ll,计算直线交点的函数,因为输入中只有直线类的几何体。在getIntersection_ll中最耗费时间的是set::insert函数。

7. Design by Contract,Code Contract

看 Design by Contract,Code Contract 的内容:
http://en.wikipedia.org/wiki/Design_by_contract
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
描述这些做法的优缺点,说明你是如何把它们融入结对作业中的。(5')

Code Contract规定软件设计人员应为软件组件定义正式,精确和可验证的接口规范,该规范应使用前提条件,后置条件和不变式来扩展抽象数据类型的普通定义。根据对商业合同的条件和义务的概念隐喻,这些规范被称为“contract”(合同)。

优点是

定义了精确的接口规范,从而使得程序移植性好,降低出错的机率。

缺点是

一些简单的函数也使用接口规范,会增加开发的负担。

我们对接口返回值的规定,用注释标注在了函数定义之前。由于大多数函数比较简单,只写了个别的函数,比如检查错误的函数返回值的含义。

8. 单元测试

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

单元测试代码

分别摘取了一个功能测试和一个异常测试,其中异常测试中测了判断输入格式的函数。

//圆与直线相切
		TEST_METHOD(TestMethod9)
		{
			Core core;
			core.addGeomrtie("C 0 0 1");
			core.addGeomrtie("L 1 0 1 1");
			core.intersect();
			set<Point>::iterator it = core.intersections.begin();
			Assert::AreEqual((int)core.intersections.size(), 1);
			Assert::AreEqual(it->first, 1.0);
			Assert::AreEqual(it->second, 0.0);
		}
//坐标越界
		TEST_METHOD(TestMethod13)
		{
			Assert::AreEqual(checkRange("L 0 0 100000 0"), 1);
			Assert::AreEqual(checkRange("L 0 0 -100000 0"), 1);
			Assert::AreEqual(checkRange("L 0 0 100001 0"), 1);
			Assert::AreEqual(checkRange("L 0 0 -100001 0"), 1);
		}

测试思路

功能测试:直线相交,直线平行,斜率无穷;两圆相离,嵌套,相切,相交;圆与直线相离,相切,相交;直线与线段相离;直线与射线相交,相离;圆内含线段,内含射线端点,圆与射线相离。

异常测试:输入图形类别出错;输参数为小数;参数个数出错;圆的半径小于零;坐标越界;直线两点相同;两直线重合;两线段重合;(在同一直线上的)两线段不重合,两线段连接;两射线重合;(在同一直线上的)两射线不重合,两射线连接。

覆盖率截图

9. 异常处理说明

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

  • 我们定义了4种异常,如下图所示:

    181585033447_.pic_hd.jpg

    1. 输入的语句不符合语法:

      WechatIMG9.png

    2. 图形的数值不符合范围:

      WechatIMG10.png

    3. 图形两点存在重合:

    4. 两个几何体有无穷交点:

      WechatIMG11.png

    对每一种异常,我们都给其定义一个返回值

    其中两个几何体之间存在交点是最复杂的,需要考虑射线的方向,线段的端点。

10. 界面模块

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

界面模块采用QT来开发。界面如下图所示,有一个窗口,三个部分:
WechatIMG12.png

  • 最左边是功能控制面板。从上到下依次为控制按钮,图形选择器,输入文本框,状态反馈窗口,调整画图版。用户首先在输入文本输入想要添加的几何体,使用语法和控制台程序一样。然后点击添加按钮,该几何体就会出现在图形选择器界面中,同时在状态反馈窗口中提示用户输入是否合法,如果合法,则会在画板中更新。除此之外,用户可以通过删除按钮,导入文件按钮来删除几何体和从文件导入几何体。

    131585026862_.pic_hd.jpg

  • 中间的部分是画图版,每一次添加删除几何体,改变窗体大小都会重新绘制图像。画图内容包括坐标轴及其刻度,几何图形的轮廓,交点。

    WechatIMG14.png

  • 右边部分是我们的显示交点信息的界面,顶部显示交点的数量,下面显示交点的数值。

    WechatIMG15.png

  • 设计过程:这是我们第一次接触UI设计的领域,严格的说之前还有设计网页UI的经历,但不同的是,软件的UI需要从头到尾自己编码而不是简单的调用模版。我们先认真的分析了用户的使用习惯,得出了一下思路:

    1. 尽量简洁,避免多余的点击操作
    2. 把所有信息显示在一个平面上
    3. 实时提供反馈

    因此我们这样设计了这个软件,具体的实施过程中我们通过看样例,搜索网络资料,自己实验等方法,从无到有积累经验完成了ui的制作。

    WechatIMG16.png

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

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

由于我们的core模块在本次调用中有两个部分,一个是错误处理函数,一个是core的主题对象,因此并不需要设计特别复杂的对接。

WechatIMG17.png

12. 结对的过程

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

尝试过Code Share,最后觉得微信交流更有效率。

13. 结对编程的优点和缺点

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

结对编程的优点:能得到更高的投入产出比

  • 开发层次,能提供更好的设计及质量和代码质量,有更强解决问题的能力
  • 对开发人员自身,能带来更多信心,以及高质量产出的满足感
  • 在心里上,结对的人不好意思开小差
  • 再企业管理层次,结对能更有效交流,相互学习和传递经验。

结对编程的缺点:

  • 能力较强者和较弱者需要磨合,强者不可避免地要多付出一些

我的优缺点:细心;思路比较周全;对ui的效果有些执着;缺点是对编译环境引发的问题一无所知

队友的优缺点:效率高!解决问题的能力强!代码结构好!暂时没有缺点

posted @ 2020-03-24 18:51  盐块QAQ  阅读(227)  评论(4编辑  收藏  举报