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,把实现一个整体功能的函数设置为public,这样的话我们从外部调用一个模块的功能就会非常直观。面向对象方面,我们的整个工程分为了三个模块,模块之间的数据关系,调用依赖都是非常的独立。也就是说,每一次工作的时候,模块之间只会通信基本的控制信息和数据流,对各个模块具体的内容没有任何影响,这也符合了信息隐藏原则。
  • Interface Design ,接口设计。接口设计是实现程序的结构化和信息隐藏原则的最重要的方法,其将模型的方法抽象出来,作为一个函数或者类的方法供外部或自己调用。小到一个解决某个问题的程序,大到一个工程项目,都离不开接口设计。这次作业,我们设计了:
    • Core类,里面定义了方法addGeomrties(string input),外部只需要传入一个字符串就可以完成添加命令,因此这个函数可以在ui,命令行,控制台都方便的调用。方法intersect()只需要在添加集合体之后调用这个函数,core模块就会自动完成所有计算,不需要外界任何调用。
    • painter类,里面定义了方法paintEvent(QPainteEvent *event),外界不需要任何的调用,在我们的框架下,系统会自动根据窗口大小,集合数据自动重新画图,甚至不需要传入参数的过程。
    • Geometry对象,里面有一个枚举类包含了圆,直线,射线等所有不同的几何体,统一的定义了计算的接口完成计算,避免过多的分类讨论。
  • Loose Coupling,松耦合。松耦合是指减少程序各个功能模块之间的依赖程度。我们通过对直线类、圆类封装并提供相关的接口来实现对直线或者圆的初始化、某两个几何对象交点求解等功能;此外,我们还将总的求解方法封装在core中,实现整体的松耦合。

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

设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。(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}\)。一种特殊情况是线段、射线所在直线重合时,由于自身长度有限可能并不重合。

五.UML类图

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

861585045485_.pic_hd.jpg

六.性能改进

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

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

871585045487_.pic_hd.jpg

881585045491_.pic_hd.jpg

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

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

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

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

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

    • 在本项目中主要通过注释的方法设计开发的计划,以便保证两个人的模块能正常的对接,一下是我们部分的约定注释。
    • 但是在实际运用中有的地方架构变化比较大,注释的方式可能存在过于死板的问题。

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

计算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到 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);

 }

测试思路

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

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

覆盖率截图

891585045494_.pic_hd.jpg

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

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

    181585033447_.pic_hd.jpg

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

      WechatIMG9.png

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

      WechatIMG10.png

    3. 图形两点存在重合:

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

      WechatIMG11.png

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

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

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

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

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

    131585026862_.pic_hd.jpg

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

    WechatIMG14.png

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

    WechatIMG15.png

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

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

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

    WechatIMG16.png

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

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

WechatIMG17.png

十二.描述结队的过程。

  • 我们一开始尝试过LIVE SHARE进行编程,但是在实际体验中发现这样对我们的效率并没有特别的提升,因此我们最终采用分工合作,主要是我来写前段部分,队友写后端部分

  • 我们在合作过程中遇到的最大的困难还是平台方面的困难,由于某些未知的原因,导致我可以运行生成的一些程序在我的队友的电脑上就会报错。我们花了大量的时间解决这样的问题,最后基本都解决了,但是还是有一些没有成功,只能采用我单独完成这部分的工作。

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

  • 结对编程的优缺点:

    • 优点:
      1. 相互学习,每个人的代码都有很实用的技巧构思,看队友的代码可以学到很多东西。
      2. 相互监督,在一起工作的时候更有责任感,需要对自己开发的东西负责。
      3. 相互检查,有的问题一个人可能并不能发现,需要两个人一起检查才能发现。
    • 缺点:
      1. 两个人工作的进度和节奏可能存在一些差别,需要调整磨合适应。
  • 我与他的优缺点:

    她(gj)
    优点 比较注重代码细节;思考慎重,比较细心;能比较快速地查找到代码的bug。 比较细心,能够发程序的bug并提出优化方案。
    缺点 缺少耐心。 面对一些困难会耗费很多时间解决。

十四.PSP回填,见二。

posted on 2020-03-24 18:34  hjh3407  阅读(287)  评论(3编辑  收藏  举报

导航