2020软工结队项目作业

2020软工结队项目作业

一.教学班级和github地址

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

二.PSP表格

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

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

  • Information Hiding,信息隐藏原则。在结构化函数的概念和面向对象的封装思想中,都深刻体现这一原则。从代码层次,是要求我们需要把每个模块用好的接口来封装,这样当外界需要使用某模块的某功能的时候,直接调用模块提供的接口,而不用了解其具体实现细节,实现代码信息的隐藏。在本次作业中,我们对所有类的所有属性都以private声明,并提供接口以使外界访问该属性成员;其次,我们将计算模块core封装起来成一个类作为单独的模块,为不同情况(如是命令行调用还是GUI,是测试模式还是正常模式)提供不同的接口,外界调用接口就可以获得想要的答案,而不需要知道内部细节。实现了信息隐藏原则。
  • Interface Design ,接口设计。接口设计是实现程序的结构化和信息隐藏原则的最重要的方法,其将模型的方法抽象出来,作为一个函数或者类的方法供外部或自己调用。小到一个解决某个问题的程序,大到一个工程项目,都离不开接口设计。这次作业,我们设计了:
    • PairCore类,里面定义了方法output1(),外部只需要给定相应的输入来初始化创建PairCore类的一个实例,再调用该方法即可获得相应的输出。当没有传入参数,默认为命令行模式,则天然支持命令行执行。
    • gui_solve函数接口,供GUI使用。
    • command_solve函数接口,供命令行使用(我们GUI模块里是将命令行参数先存储在一个文件里,然后该接口里就打开指定的文件获取命令行参数)。
  • Loose Coupling,松耦合。松耦合是指减少程序各个功能模块之间的依赖程度。我们通过对直线类、圆类封装并提供相关的接口来实现对直线或者圆的初始化、某两个几何对象交点求解等功能;此外,我们还将总的求解方法封装在core中,实现整体的松耦合。

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

  • 代码组织。

    • 我们定义了6个类:

      • Point:用于管理坐标系的中的点。
      • HashPoint:定义hash函数,以方便使用unorder_set来存储交点。
      • Line:直线类。用于管理直线、射线、线段。三者我们会定义一个LineType来表明"直线"的类型。
      • Circle:圆类。用于管理圆。
      • PairCore:核心计算类,通过初始化输入输出路径,管理总的几何对象并求解交点,若有异常则返回相应的异常。
      • Exception:包括CoincideException,InputException,CommandException。分别是几何对象性质出错(重合,即无数个交点),输入异常以及命令函参数异常。
    • 各类中的主要方法(函数)列表如下:

      函数(方法) 所属类 主要功能说明
      bool operator==(const Point& p) const Point 重写等于比较
      bool operator<(const Point& p) const Point 重写小于比较
      int relation(Line l) Line 比较两个直线类几何对象的关系(平行、重合或者相交)
      vector<Point> getIntersect(Line l) Line 求解两条直线的交点集合(1个或0个,重合报错)
      vector<Point> getIntersectWithLine(Line line) Circle 求解圆和直线的交点集合
      vector<Point> getIntersectWithCirc(Circle circ1) Circle 求解圆和圆的交点集合
      int parser(int argc, char* argv[]) PairCore 处理命令行参数
      void text_handle() PairCore 处理输入,并将集合对象存储在容器中
      int getIntersectionCount() PairCore 对容器的几何对象进行交点求解并返回交点个数
      int output1() PairCore 将PairCore中所有求解部分封装,外界只需要调用该接口即可获得答案。
    • 除了类内的方法,我们定义了两个函数:

      • gui_solve:为了GUI而设计的接口。
      • command_solve:为了命令行而设计的接口。
    • 函数间关系:

      • 最顶层的是output1()parser():先根据是否是命令行模式来决定是否调用parser。然后调用output1()
      • output1先调用text_handle,处理完输入后,调用getIntersectionCount()求解。
      • getIntersectionCount()则是遍历容器,根据几何对象的类型相应调用圆类的getIntersectWithLine(Line line),getIntersectWithCirc(Circle circ1)或者直线类的getIntersect(Line l)来求解交点。
  • 算法的关键

    • 关键在于判断几何对象之间的位置关系(重点在于重合)和交点的求解。
      • 直线类间的位置关系:平行、重合或者相交。根据解析式Ax+By+C = 0的A,B,C的关系判断即可。
      • 圆类间的位置关系:相交、相离、相切。根据两圆的半径长度与两圆心之间的距离关系来判断。
      • 直线与圆:相交、相离、相切。求出圆心到直线的垂直距离,和圆的半径比较即可。
      • 交点求解:
        • 由于我们是根据解析式中A,B,C的关系来判断两直线类几何对象的位置关系,所以对于线段和射线,当判断到重合的时候,还需要进一步判断端点、方向之间的关系。
        • 判断好位置关系后,剩下的就是代入公式求解。仍沿用个人项目作业中的求解方法。求解方法参考:个人项目作业第五部分。这里不再赘述。
    • 算法独到之处:对于线段,我们比较了其两个端点的坐标关系,来定义其左端点和右端点;对于射线,我们比较了其端点和过的一点的坐标关系,定义其起点和方向。这样做方便我们对直线类集合对象的位置关系求解以及判断求出的交点(我们求解直线之间的交点的办法是先将两条线当作直线处理,再判断交点是否都在两个“直线类”几何对象上)是否在集合对象上

五.UML类图

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

我们在计算模块接口性能改进大概花了20分钟左右,因为是沿用个人项目作业的算法,所以对算法没有进行优化。优化主要是在于代码中数据结构的选用以及细节问题。

经过一开始的代码性能分析,发现居然是Line类里面的求解线与线之间的交点的方法getIntersect占的时间最多,这不符合我们的预期。而理论上应该是在管理交点的unorderedset处,进行插入操作的开销时间最大。

进一步观察发现求交点的时候我们声明了一个容器来保存直线与直线之间的交点,个数为0或者1,而这两句CPU开销占比很大,所以我们修改该方法,直接返回一个点或者返回一个打上标志位的点(表示没有交点)。下面两张图分别展示了对这一点进行优化前后的性能分析图:

可以发现优化后的Line类里面的求解线与线之间的交点的方法getIntersect已经不在第二张图中,并且对于同一个数据时间有所减少,说明对该方法的优化是有效的。

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

  • 契约式设计,是一种设计计算机软件的方法。这种方法要求软件设计这为软件组建定义正式的、精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。

    • 前置条件:为了调用函数,必须为真的条件,在其违反时,函数决不调用,传递好数据是调用者的责任。
    • 后置条件:函数保证能做到的事情,函数完成时的状态,函数有这一事实表示它会结束,不会无休止的循环
    • 不变式:从调用者的角度来看,该条件总是为真,在函数的内部处理过程中,不变项可以为变,但在函数结束后,控制返回调用者时,不变项必须为真。
  • 优缺点:

    • 优点:
      • 获得更优秀的设计
      • 提高代码的可靠性,有利于测试
      • 为不同模块约定其与其他模块的接口及其应该有的性质,方便模块之间的整合
      • 契约式设计可以明显降低调试的代价,能比较准确地定位错误
      • 支持复用
    • 缺点:
      • 契约式设计需要比较大的时间开销。
      • 小型项目中契约式设计似乎成为了一种限制,因为契约式设计较大的时间开销拖慢了整个小项目的进度。
  • 在本次结对项目中的运用:

    • 主要是在core部分进行外部接口的约定和设计,以方便和GUI模块的对接,以下是我们负责GUI模块的队友与我约定的接口设计要求。

    • 但是最后在command这个接口上,想要直接传入命令行参数不太容易,于是队友在GUI模块中将命令行参数保存在command.pair中,而我就负责将这个文件中的命令行参数提取出来,放到argv这个指针数组中。

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

  • 我们计算模块中提供了insert方法,来直接插入集合对象,而不用从文件读取;同时计算模块提供直接返回交点个数的接口getIntersectionCount,测试主要针对这两个方法展开。

  • 主要测试的是:列举线段,射线,直线和圆的各种组合,包括交点不在某线段或者射线上的情况,以及边界测试,综合测试等,一共构造了41个测试样例(包括了异常测试)。部分单元测试代码如下图:

  • 测试结果:

  • 测试的覆盖率:

    可以发现覆盖率达到了90%以上,说明单元测试比较充分。

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

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

    对每一种异常,我们都给其定义一个返回值-13 ~ -1;

  • 对于异常的具体测试样例如下图:

测试结果在第八部分中,均通过。[命令行参数错误使用在GUI模块中,这里没有对之进行测试]

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

界面模块采用C#的Winform来开发。界面如下图所示,有两个窗口:

  • 第一个窗口(Form1)是输入输出图形信息的窗口。当输入的几何对象发生几何关系异常或者输入的文件格式不正确时,就会弹出一个错误提示框。从文件导入几何信息后,先将文件中的几何信息加入到现有的几何列表中,提高了可操作性-可以灵活地根据用户所需来选择添加或者删除某个几何图形等。

  • 第二个窗口用于绘制我们的结果。用winform的panel进行绘制,由于没有成熟的坐标系模块供我们使用,所以我们的绘制过程时造轮子的过程,效果也不尽人意(不支持缩放和平移)。程序会将所有的图形都画入窗口,如果图形横纵坐标跨度大那么观感会有所欠缺。轮子中的关键代码是如何掌握好窗口的边界位置。我们相当于要将所有图形的坐标范围(minx,miny)-(maxx,maxy)映射到我们的画板(0,0)-(720,720)上。映射过程的核心代码如下:

十一.界面模块与计算模块的对接。

core模块为UI设计了两个外部接口:

我们定义了文件读入的标准,这是因为C#和C++标准有诸多不同,如C#不支持指针,char为两个字节长度等等,导致我们在传参方面始终达不到预期的效果。

十二.描述结队的过程。

  • 一开始我们尝试了使用LIVE SHARE来进行结队编程,由于网络不稳定,导致我们经常有一个人断线;之后采用code share,虽然有所改善,但是code share不提供查错功能,整体编写代码的效率很低。由于线上环境的不友好,导致效率低下,所以在尝试结队两天后我们决定分工:由我来进行后端计算模块的编写,由我的队友来进行前端UI的编写。

  • 拿到项目后,我们首先通过QQ语音与腾讯会议详细地讨论了扫描线算法,并尝试实现以争取性能分。但是我们发现扫描线算法原理虽然不难,但是实现起来要考虑过多的细节,除了要考虑除了参考资料中对线段的四种特殊位置的限制,还要考虑直线和射线,其次还有一个至关重要也是最难处理的部分:如何以O(logn)的复杂度去实现扫描线在移动过程中的状态的有序性。在经过三天晚上的跌跌撞撞后,我们最后放弃了扫描线,采用暴力求解方式。

  • 一些结队图像资料:

十三.看教科书和其它参考书,网站中关于结对编程的章节,说明结对编程的优点和缺点。同时描述结对的每一个人的优点和缺点在哪里(要列出至少三个优点和一个缺点)。

  • 结对编程的优缺点:

    • 优点:互相监督,相互push,提高开发的效率;相互测试,互相照应,提高项目的可靠性;一起复审,一起思考,编写代码时考虑更全面,从开发时就避免了大量bug。
    • 缺点:当两人编码风格不同而去编码同一个模块的时候,会有些不习惯,需要一定的思考和磨合时间。其次,针对这次结对项目,由于线上环境,网络等原因导致结对时浪费了许多时间。
  • 我与他的优缺点:

    他(gj)
    优点 比较注重代码细节;思考慎重,比较细心;能比较快速地查找到代码的bug。 编码能力强;擅长学习新知识并能够快速应用;领导力强,执行力强。
    缺点 编码能力有所不足,对C++不是很熟悉。 缺少一点耐心。

十四.PSP回填,见二。

十五.附加题:松耦合。

我们选择交换的dll的团队是(17373052,17373053)的团队。

他们的dll除了文件的命名方式与我们有所差别,其他设计方面几乎一致。

合并时遇到的问题有:

  • 他们并非由GUI和command两个不同接口构成,而是只有一个接口函数run(),由命令行参数进行区分。无论是否需要绘制图像,他们都会将信息存储在points.txt中,于是我们修改了我们读写方式,方便与他们的计算core模块对接。
  • 他们的异常处理返回信息和我们不一致。他们的run()函数只定义了两种异常:输入异常和重合。为此我们修改了异常的返回信息。

经测试,命令行和GUI都可以正常使用。

十六:关于代码要求的截图:

消除 Code Quality Analysis 中的所有警告

posted @ 2020-03-23 23:04  七月星河  阅读(267)  评论(2编辑  收藏  举报