软工结对作业

1、简介

项目 内容
这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建)
这个作业的要求在哪里 结对项目作业
我在这个课程的目标是 学习软件工程相关知识,培养自己独立和团队开发能力
这个作业在哪个具体方面帮助我实现目标 将理论课上所学的结对编程知识付诸实践,初步熟悉与别人合作开发软件的流程
作业正文...... 见下文
教学班级 005
项目地址 https://github.com/yangjiuchun123/SE_pair_homework

2&14、PSP2.1表格

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

3、接口设计

对于函数的接口,首先我们对直线类(包括直线、射线和线段)和圆类进行属性的私有化,即将其中的成员属性设置为private,并提供一些get函数来取得这些值,这样做符合信息隐藏(「Information Hiding」)的思想,可以避免类的属性被随意修改。此外,考虑到之后还需要接入错误处理和GUI模块,因此对于输入的处理也需要实现一个类中的方法,而不是像第一次作业那样直接放在main函数里。

对于松耦合(「Loose Coupling」),这点在dll文件的使用中可以体现。dll是Dynamic Link Library的缩写,即动态链接库。由于本题中涉及到多个模块,因此可以将其中最核心的计算模块做好之后生成dll文件,其他模块只要调用这个dll文件即可实现对计算模块的调用。如果计算模块以后需要更改,那么更改完后只需重新生成并更新dll文件即可,不需要改动其他模块的代码。这体现了不同代码块之间的松耦合。

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

本次作业基本上是以我上次作业的代码为基础,进行一些增量修改得来的。本次作业与之前个人项目对计算模块的要求的区别在于添加了射线线段的输入,对于这两个新的需求,我们考虑仍然使用个人作业中的两直线交点计算方式,首先都先将这两个射线、线段都视为直线,计算出如果是直线,那么它们的交点坐标是多少。然后再根据射线的起点和线段的端点坐标,与求出的交点坐标进行比较,以确定这个交点在不在射线、线段上。由于射线和线段一定是单调递增或单调递减的,因此可以通过简单的横纵坐标比较来得出结论。需要注意的是,一般情况下比较横坐标即可得出结论,但在斜率为0时需要对纵坐标进行比较才能判断。

对于两条线的交点,需要判断这个交点是不是同时在这两条线上;而对于线与圆的交点,只需判断这个交点是不是在线上即可。如果判断结果为真,即可将这个交点加入到交点集合中,否则不加入到集合中。

独到之处倒是谈不上,不过个人认为这种判断方式还是比较简单暴力的。诚然也可以使用一些方法来判断两条线段或射线是否有交点,之后再进行交点坐标的计算,但那样做可能会使代码的逻辑更加复杂,测试起来的难度也更高一些。

具体的类和其中的属性、函数可参考下面的UML图。

5、计算模块的UML图

UML图如下(Line类中一些不是很重要的属性及方法已省略):

6、计算模块接口部分的性能改进

在计算交点的部分,能改进的地方并不是很多(虽然这部分是时间开销最大的),这一点也在个人作业中提到过了。这部分做的改进就只是将一些需要重复计算的值先计算出来,以便后续直接使用,但这样的优化效果也有限。

另外,我们还做的一点优化是使用list而不是数组来存储输入的图形。固然,数组的存储、取值操作肯定是要比list快的,这也是我在个人作业中采用数组来存储图形的原因,但是这次考虑到UI的模块有添加和删除对象的需求,我们知道数组在插入和删除元素时是非常复杂的,尤其是删除元素,需要将被删除元素的后面所有元素都向前移动,这不但实现起来复杂,还可能会出现bug。因此我们认为使用list是一个较好的选择,c++的list使用双向链表实现,使得它在插入和删除元素的时候更加简单。这一点与其说是性能上的改进,不如说是因需求的改变而导致的必要的措施。

7、契约式设计

契约式设计(Design by Contract)是指要求软件设计者为软件组件定义正式的,精确的并且可验证的接口。具体来说,它为一个接口增加了先验条件、后验条件和不变式,以保证「契约」不被破坏。

契约式设计的优点在于,它可以让程序员对自己所要实现的接口的输入、输出有更加明确的认识,只要先验条件被满足,接口实现起来就可以少考虑很多错误的输入,同样,在输出方面,接口还需要保证自己的输出满足后验条件,以确保其他代码能接收到正确的结果。从这方面讲,契约式设计也给程序的测试带来了很大的方便,测试时,可以通过检查契约有没有被破坏,来确定程序是否有输入输出格式上的bug。

契约式设计的缺点在于,撰写契约本身可能会花上一段时间,而且如果在设计接口时并没有想好这个接口的契约应该如何设计,那么之后不但需要花更多时间去改契约,之后才能去改代码实现。这样一来开发的效率就会降低。

对于我们的结对项目,我认为需要制定契约的接口并不多,而且由于团队人数只有两个人,而且结对开发过程中两个人的交流非常频繁,因此可以通过简单的口头商定来确定各个接口的输入输出。此外,由于有错误处理模块的存在,对于先验条件这部分的要求并不是很严格,输入格式如果错误会直接触发错误处理,抛出异常。

项目中主要需要制定契约的部分是各个模块的衔接,如计算模块到UI模块的衔接,需要保证计算模块实现了对应的接口来供UI模块调用,这些接口是需要一些条件保证的,为此我们特别开会讨论并确定了接口的输入、输出应该是怎么样的。

8、计算模块部分单元测试

单元测试还是采用构造各种不同类型的输入来进行,例如直线与直线、直线与线段、射线与线段、射线与圆、线段与圆等情况来构造。构造的情景有利两线相交、平行,两圆相交、相切、相离等。还构造了一些需要特殊处理的情况,如线段的斜率为0、线段的斜率不存在等情况,这些情况在程序中是需要特判的。

测试代码及测试覆盖率截图如下:

9、计算模块部分的异常处理

异常处理部分,我们考虑了的异常和对应的提示信息如下表所示:

异常 提示信息
输入格式错误——首行格式错误 Wrong input format: expect a positive integer in the first line!
输入格式错误——其他行格式错误 Wrong input format: expect "L/R/S (integer) (integer) (integer) (integer)" or "C (integer) (integer) (integer)"
点的坐标范围超限 Invalid input: point out of range(-100000, 100000)!
直线的两点重合 Invalid input: you entered two same point for a line!
有无穷多交点 Error when computing intersections: there are infinite intersections!

这部分单元测试的样例如下:

10、界面模块的详细设计过程

注:该问题和下一个问题转载自我的结对伙伴的博客

​首先因为之前没有前端开发经验,所以,在网上搜集了许多资料,最后选择QT作为开发工具,并且购买了一本入门级的参考书。经过一系列曲折,决定采用dialog和button的形式实现。

第一个对话框实现按钮,即具体功能,见下图:

通过get Address获得需要打开的文件,通过img获得几何图像(显示新的dialog),通过add obj添加新的几何对象,通过delete number减少新的几何对象,点击number of intersets来获得交点数。

关于这部分的两个对话框代码设计如下:

这是第一个窗口的设计代码:

class calculate :public QDialog {
    Q_OBJECT
public:
    calculate(QWidget* parent = 0);
signals:

private slots:
    void getFile();
    void removeObj();
    void addObj();
    void showResult();
    void showImg();
    
private:
    QString Filename;
    QPushButton* getAddress;
    QPushButton* num;
    QPushButton* del;
    QPushButton* add;
    QPushButton* img;
    QLineEdit* info;
    QLineEdit* delnum;
    QLineEdit* result;
};

这是第二个窗口的代码:

class picture :public QDialog {
public:
    picture(QWidget* parent = 0);

private:
    void paintEvent(QPaintEvent*);
};

只是重构了paintEvent函数,用于在第二个dialog实现画图。

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

主要使用了core.dll中的Solve::solve和Solve::addList,以及Solve::deleteList这三个方法

void calculate::removeObj() {
    QString input = delnum->text();
    int temp = input.toInt();
    sol->removeList(temp);
}

void calculate::addObj() {
    QString input = info->text();
    string information = input.toStdString();
    char type;
    int x1, y1, x2, y2, x, y, r;
    if (information[0] == 'C') {
        if (sscanf(information.c_str(), "%c %d %d %d", &type, &x, &y, &r) == NULL) {
            return;
        }
        Circle* newcircle = new Circle(x, y, r);
        sol->addShape(newcircle);
    }
    else {
        if (sscanf(information.c_str(), "%c %d %d %d %d", &type, &x1, &y1, &x2, &y2) == NULL) {
            return;
        }
        Line* newline = new Line(type, x1, y1, x2, y2);
        sol->addShape(newline);
    }
}

void calculate::showResult() {
    result->setText(QString::number(sol->getSolve()));
}

12、结对的过程

结对过程中,我们主要是用腾讯会议来进行两个人的沟通,同时使用屏幕共享功能来互相阐述自己的想法和思路。整个项目基本上是一人写代码,另一人复审,过一段时间再两人互换角色这样进行的。

会议的截图如下:

13、结对编程的优缺点

结对编程的优点在于:

(1)两个人可以一起分享思路,并且指出各自思路中的不足,给出建议,达到取长补短的效果;

(2)两个人可以互相监督进度,不至于一起拖着到最后ddl了才开始做;

(3)写代码和代码审查可以在几乎同时进行,使得开发效率大大提高了。

缺点在于:

(1)两个人的思路如果差的很多的话,可能需要花费较多时间来统一思路;

(2)即使统一了思路,在一些细节的实现上可能需要花费较长时间来读懂同伴的代码;

(3)两个人的性格、关系等因素可能会影响结对编程的效率。

具体到我和我的结对编程伙伴上,我认为我的优点在于:

(1)做事不拖沓,可以督促自己和伙伴的进度;

(2)表达能力较强,能够向同伴说明白自己的思路;

(3)性格随和,和同伴能搞好关系,提高结对的效率。

我的缺点在于:

(1)考虑经常会有不周到的地方,很容易出bug;

(2)不是很擅长看别人的代码,有时候要花较长时间才能看懂。

我的结对伙伴的优点在于:

(1)思维比我缜密,可以发现代码中的bug;

(2)学习能力强,图形化界面很大部分是他实现的;

(3)做事也不喜欢拖沓,这点和我一样。

他的不足在于:

(1)语言能力相对差一些,有时候比较难get到他说话的重点。

附:消除Code Quality Analysis警告的截图

posted @ 2020-03-23 21:58  杨久春  阅读(177)  评论(2编辑  收藏  举报