[敏捷软工结对项目博客]第二版平面图形求交点

项目 内容
所属课程:北航-2020-春-软件工程 博客园班级博客连接
做业要求:计算平面图形的公共点个数——新需求 结对项目做业要求
我在这个课程的目标 提高在团队合做中开发“好软件”的能力
这个做业在哪些具体方面帮助我 根据《构建之法》第四章两人合做的内容,完成结对项目。培养软件工程、团队合做相关的能力。
教学班级 005
项目地址 https://github.com/Cheibniz/PairProject

1、PSP表格

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

2、软件设计原则

1.information hiding

在维基百科中,有这样一段关于information hiding的描述java

In computer science, information hiding is the principle of segregation of the design decisions in a computer program that are most likely to change, thus protecting other parts of the program from extensive modification if the design decision is changed. The protection involves providing a stable interface which protects the remainder of the program from the implementation (the details that are most likely to change).python

Written another way, information hiding is the ability to prevent certain aspects of a class or software component from being accessible to its clients, using either programming language features (like private variables) or an explicit exporting policy.ios

总结一下,细节隐藏是对计算机程序中最容易变化部分的设计方法。在细节隐藏原则下,使用者只调用肯定的模块接口,而模块内部的具体实现则被隐藏。类的私有成员等和明确规定的接口都是细节隐藏原则的体现。c++

面向对象思想本就带有很强的工程气息,细节隐藏原则蕴含其中。在用面向对象的编程语言编程以后,我的就能对细节隐藏思想有所领会。c++javapython等编程语言都有对类和接口/抽象类的语言特性支持。git

在本次结对编程项目中,咱们在一些层面的设计中体现了信息隐藏原则github

层面 体现
函数和变量尽量私有,只将完成类功能的函数公有
模块(几何图形容器、文件读取模块等) 模块具备必定复杂度,实现模块功能的函数和方法全都私有/使用者不可见。只对使用者开放完成模块功能的接口(主要是函数)
先后端接口 将几个函数做为接口,而且函数的设计尽可能简单灵活(参数和返回值都使用C++标准库中存在的类型,如stringvectorpair

2.interface design

本次做业的一大挑战是对先后端接口的设计。先后端各自完成复杂且具备必定规模的任务,如何完成灵活高效的接口是一个须要思考的问题。正则表达式

首先,使用接口不可避免地使得软件的性能下降,因此在设计接口时须要对性能损失有所容忍。相比起少许的软件性能损失,接口赋予软件的可维护性、鲁棒性等更加剧要。算法

优秀的API接口设计原则及方法有以下论述编程

一旦API发生变化,就可能对相关的调用者带来巨大的代价,用户须要排查全部调用的代码,须要调整全部与之相关的部分,这些工做对他们来讲都是额外的。若是辛辛苦苦完成这些之后,还发现了相关的bug,那对用户的打击就更大。若是API常常发生变化,用户就会失去对提供方失去信心,从而也会影响目前的业务。后端

能够看出,在设计接口时须要对接口的灵活性加以考量。在本次做业中,附加做业要求先后端接口被两个组共同使用,咱们采用规格化字符串的手段使接口灵活、轻量。

#pragma once
#include <vector>
#include <iostream>

using namespace std;
typedef pair<double, double> InterfacePoint;

// parameter = file_name
__declspec(dllexport) void readFile(string);

// add from standard string fromat of geometry component
__declspec(dllexport) void addGeometryObject(string);

// remove by standard string format  of geometry component, strictly equals
__declspec(dllexport) void removeGeometryObject(string);

// trigger calculation
__declspec(dllexport) pair<vector<string>, vector<InterfacePoint>> getResult();

使用接口须要共同的约定,而在本次做业中两小组间存在天然的共同约定:做业要求。做业要求博客中给出几何对象的格式化字符串定义,而格式化字符串定义是绝佳的接口交换数据结构。对于返回值中承载多个对象及课程组未进行点的格式化字符串定义,因此咱们使用C++标准库中存在的类vectorpair

在设计结构时咱们发现,对接口的要求不只有输入和返回值,还有函数的行为和可能抛出的异常。对于此方面的考量在Loose Coupling中描述。

3.Loose Coupling

Loose Coupling的WIKI百科中,有这样一段描述

In computing and systems design a loosely coupled system is one in which each of its components has, or makes use of, little or no knowledge of the definitions of other separate components. Subareas include the coupling of classes, interfaces, data, and services.[1] Loose coupling is the opposite of tight coupling.

松耦合是老师同窗们时常挂在嘴边的“软件设计”原则,可是这与软件中不一样模块之间互相依赖关系有所矛盾。怎样才能作到松耦合呢?咱们认为“依赖于抽象”是作到松耦合的好方法。

仍是以上文提到的先后端接口为例,对接口的考虑不止有输入输出,还有函数的行为和可能抛出的异常。函数的输入和输出可以显示地包含在函数定义中,那函数的行为和异常怎么办呢?答案就是“依赖于抽象”,关注函数在抽象层面的行为,忽略函数具体地行为。例如__declspec(dllexport) void readFile(string);,咱们只须要指望这个函数被调用后,文件中的几何对象都被读取和处理完成,并不须要考虑文件具体状况对函数行为的影响和调用模块内部是怎样完成几何对象的处理和求解的。

异常实质上也是函数的一种返回值,一样能够在函数声明中写出。对异常的约定与对返回值的约定类似,但异常不少地包含函数运行状态的信息。因此对异常的约定须要更多地对函数行为的约定,实际上增长了模块之间的耦合度。异常一样符合“依赖于抽象”原则,若是模块须要给其余模块抛出异常,那么该模块自身不能处理。这种异常通常是与模块实现细节无关的异常,可以影响甚至中断函数执行。因此只要将接口中的异常约定为针对函数抽象行为的异常,就不会显著地增长软件的耦合度。

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

1.几何图形模块

相较于上次做业,本次做业多出射线和线段,须要对几何图形的类结构进行增量设计。

咱们约定直线方程以下:

\[Ax+By+C=0,\quad A^2+B^2+C^2=1,A\geq0 \]

通过归一化后的直线系数能为后续的直线共线判断提供便利。

咱们约定圆方程以下:

\[(x-x_0)^2+(y-y_0)^2=r^2,\quad r>0 \]

对于全部的几何对象(除点),都须要实现以下功能

  • 从规格化字符串或浮点数参数创建几何对象
  • 求交点
  • 判断点是否在对象上
  • 重写==运算符
  • 重写<运算符
  • 生成格式化字符串

下面将针对和上次做业不一样的部分进行阐述

类设计

几何对象 设计
直线 其实是3个系数的封装
射线 在直线的基础上添加起点和方向
线段 在直线的基础上添加两个端点
圆心和半径的封装
pair<double, double>的封装

判断相等

不一样种类的几何对象确定不相等,相同种类的几何对象则判断数学上是否相同。

求交点

直线 射线 线段
直线 直接联立求交点 联立求出交点后判断交点是否在射线上 联立求出交点后判断交点是否在线段上 直接联立求交点
射线 联立求出交点后判断交点是否在射线上 1.不共线:联立求出交点后分别判断是否在射线上。2.共线:判断是否只有一个交点。 1.不共线:联立求出交点后分别判断是否在射线和线段上。2.共线:判断是否只有一个交点。 联立求出交点后判断交点是否在射线上
线段 联立求出交点后判断交点是否在线段上 1.不共线:联立求出交点后分别判断是否在射线和线段上。2.共线:判断是否只有一个交点。 1.不共线:联立求出交点后分别判断是否在线段上。2.共线:判断是否共端点。 联立求出交点后判断交点是否在线段上
直接联立求交点 联立求出交点后判断交点是否在射线上 联立求出交点后判断交点是否在线段上 直接联立求交点

射线-射线共线有交点

射线-线段共线有交点

线段-线段共线有交点

2.几何对象容器

几何对象容器的做用是组织几何对象和交点,其功能以下

  • 存储组织几何对象与交点

  • 增删几何对象

  • 求解交点

几何对象容器在内部完成复杂的几何对象交互逻辑,对外部只须要开放简单接口便可。这样设计屏蔽几何对象相关细节使得修改具体实现变得容易,体现出information hidingLoose Coupling的思想。

几个对象容器的接口以下

接口名 做用
insertFromString 由规格化字符串插入几何对象
deleteFromString 由规格化字符串删除几何对象
getPointNum 得到交点数量
getPointSet 取出交点集合
getLineSet 取出线(规格化字符串)集合
getCircleSet 取出圆(规格化字符串)集合

3.IO类

检查文件是否合法(有无n,n和实际几何对象数量是否相符),处理IO异常(文件不存在、IO错误等)。屏蔽IO细节,错误局部化。

4.UML

4、性能改进

1.算法层面

暴力求解,算法复杂度为\(O(n^2)\),在短期内没有改进的空间。

2.实现层面

2000个几何对象,四类几何对象出现几率均等。

  • 优化一:从哈希到红黑树。在上个项目中使用的是unordered_set,理论上是\(O(1)\)的复杂度,可是实际应用起来结果却惨不忍睹。猜想多是哈希操做带来的大量主存访问占用大量时间。采用set以后,虽然容器访问还是时间占用主体,可是相比哈希占比已经降低不少。
  • 优化二:优化正则表达式。在仔细阅读做业要求博客以后,将一些正则表达式中没必要要的部分去除,从而略微减少正则表达式匹配所占用的时间。

3.接口问题

在引入先后端接口以后,带来了必定的性能损失。由于如今所采用的接口是两个小组通用的,因此两个小组都为适配接口增长一层适配函数。在不对后端进行重构的状况下,咱们很难将这部分性能损失彻底抹除。只能在适配时采用内联等措施减少性能损失。

彻底消除性能损失也许须要大量的项目经验和相关思考,但遗憾的是咱们没能在这次做业中作到。

5、Design by Contract和Code Contract

Design by Contract的WIKI百科中,有以下描述

It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants. These specifications are referred to as "contracts", in accordance with a conceptual metaphor with the conditions and obligations of business contracts.

承接在2、软件设计原则 中的讨论,接口须要规定以下几个部分:参数、返回值、行为、异常,这是针对函数而言。而契约式设计则在ADT的基础上加入前置条件、后置条件和不变式的约束,让开发者和使用者都有更加形式化、准确及可验证的依据。契约式设计的精准性和形式性为程序验证作好了准备,微软爸爸在.NET平台的基础上提供了相应工具

在去年的面向对象课程中,咱们也曾使用过JML进行契约式编程,因此对契约式编程的感觉颇深。

契约式编程的优势有

  • 为开发者提供明确的依据,可以具体地指导开发和测试过程。
  • 为使用者提供可靠的接口,在不了解模块具体实现的条件下就能够对模块的数据行为有所把握。
  • 支持自动验证,与形式化验证类似,给出测试以外的保证软件稳定性的手段。
  • 责任分明:出现问题时,可以很快找出开发者仍是使用者哪一方有问题,不会有扯皮的状况出现。

而其缺点也是不可忽视

  • 极大的增长契约设计者的工做量:设计一个完备恰当的契约须要耗费设计者不少精力,考虑模块可能被使用的各类情形。
  • 须要极其成熟可靠兼容性高的验证工具:对于开发者来讲,人工手动验证契约须要花费巨大的代价,甚至对开发者是一种折磨。若是验证工具不能工做,那么开发者将陷入窘迫的境地。

在咱们的项目中,最能体现契约式编程的部分是几何对象的构建。几何对象的行为都严格按照其抽象特性设计。如今编写几何对象的方法式屡次按照直线、射线、线段、圆的抽象特性进行检查,从而保证几何对象在程序中的行为和数学上的行为相同。

结合对象的数学抽象特性使得其天然具备契约,可以为具体设计提供很好的参考和指导。

6、单元测试

针对不一样的功能点和错误点,咱们准备了不一样的单元测试。

1.编译器报错与警告清零(Code Quality Analysis)

2.单元测试的总体状况以下

测试覆盖率92%

测试用例经过状况

3.具体测试用例(部分展现)

针对几何对象相交功能直接测试

// 射线端点与直线相交
TEST_METHOD(TestMethod1)
{
    Table table = Table();
    Straight s(0, 0, 1, 1);
    Ray r(0, 0, 0, 1);
    int result = s.GetCrossPoint(table.getPointSet(), &r);
    Assert::AreEqual(result, 1);
}

// 射线和线段端点与圆相交
TEST_METHOD(TestMethod3)
{
    Table table = Table();
    Circle c(Point(0, 0), 1);
    Segment s(0, -1, 0, 1);
    Ray r(-1, 0, 5, 0);
    int result = c.GetCrossToLine(table.getPointSet(), s);
    Assert::AreEqual(result, 2);
    Assert::AreEqual(c.GetCrossToLine(table.getPointSet(), r), 2);
    Assert::AreEqual(r.GetCrossPoint(table.getPointSet(), &s), 1);
}

// 圆之间内切与外切
TEST_METHOD(TestMethod4)
{
    Table table = Table();
    Circle c(Point(0, 0), 1);
    Circle c1(Point(3, 0), 2);
    Circle c2(Point(2, 0), 1);
    c.GetCrossToCircle(table.getPointSet(), c1);
    c1.GetCrossToCircle(table.getPointSet(), c2);
    Assert::AreEqual(1, (int)(table.getPointNum()));
}

总体性测试 + 异常测试

TEST_METHOD(TestMethod6)
{
Table table = Table();
ofstream output = ofstream("output.txt");
output << "4\n C 0 0 1\nC 3 0 2\nC 2 0 1\nL 0 0 3 0\n" << endl;
output.close();
ifstream open = ifstream("output.txt");
// IO模块测试
readFromFile(table, open);
Assert::AreEqual(4, (int)table.getPointNum());
Circle circle = Circle(Point(0, 0), 1);
// 删除圆功能测试
table.eraseCircle(circle);
Assert::AreEqual(3, (int)table.getPointNum());
circle = Circle(Point(2, 0), 1);
// 删除圆功能测试
table.eraseCircle(circle);
Assert::AreEqual(2, (int)table.getPointNum());
Line* line = new Line(0, 0, 1, 0);
try
{
// 无穷多交点异常测试
table.insertLine(*line);
}
catch (const Doublication& e)
{
Assert::AreEqual(line->toString(), (string)(e.what()));
}
Assert::AreEqual(2, (int)table.getPointNum());
// 删除线功能测试
table.eraseLine(line);
Assert::AreEqual(0, (int)table.getLineSet().size());
}

TEST_METHOD(TestMethod7)
{
    Table table = Table();
    ofstream output = ofstream("output.txt");
    output << "4\n C 0 0 1\nC 3 0 2\nC 2 0 1\nL 0 0 3 0\n" << endl;
    output.close();
    ifstream open = ifstream("output.txt");
    readFromFile(table, open);
    Circle circle = Circle(Point(0, 0), 1);
    try
    {
        // 无穷多交点异常测试
        table.insertCircle(circle);
    }
    catch (const Doublication & e)
    {
        Assert::AreEqual(circle.toString(), (string)(e.what()));
    }
}

Table table = Table();
ofstream output = ofstream("output.txt");
output << "1\nL 0 0 0 0\n" << endl;
output.close();
ifstream open = ifstream("output.txt");
try
{
    // 定义点重合异常测试
    readFromFile(table, open);
}
catch (const pointDoublication& pd)
{
    Assert::AreEqual(Line(0, 0, 0, 0).toString(), (string)pd.what());
}

易错点测试

TEST_METHOD(TestMethod11)
{
    Table table = Table();
    ofstream output = ofstream("output.txt");
    // 线段垂直于X轴,射线垂直于y轴,可能会形成点位置判断错误
    output << "2\nR -1 0 1 0\nS 0 1 0 2" << endl;
    output.close();
    ifstream open = ifstream("output.txt");
    readFromFile(table, open);
    Assert::AreEqual(0, (int)table.getPointNum());
}

新增需求特殊点测试

// 线段-线段共线有焦点
TEST_METHOD(TestMethod15)
{
    Table table = Table();
    ofstream output = ofstream("output.txt");
    output << "2\nS 0 0 1 1\nS 1 1 2 2" << endl;
    output.close();
    ifstream open = ifstream("output.txt");
    readFromFile(table, open);
    Assert::AreEqual(2, (int)table.getLineSet().size());
    Assert::AreEqual(1, (int)table.getPointNum());
}

// 线段-射线共线有焦点
TEST_METHOD(TestMethod16)
{
    Table table = Table();
    ofstream output = ofstream("output.txt");
    output << "2\nS 0 0 1 1\nR 1 1 2 2" << endl;
    output.close();
    ifstream open = ifstream("output.txt");
    readFromFile(table, open);
    Assert::AreEqual(2, (int)table.getLineSet().size());
    Assert::AreEqual(1, (int)table.getPointNum());
}

// 射线-射线共线有交点
TEST_METHOD(TestMethod17)
{
    Table table = Table();
    ofstream output = ofstream("output.txt");
    output << "2\nR 1 1 0 0\nR 1 1 2 2" << endl;
    output.close();
    ifstream open = ifstream("output.txt");
    readFromFile(table, open);
    Assert::AreEqual(2, (int)table.getLineSet().size());
    Assert::AreEqual(1, (int)table.getPointNum());
}

7、异常处理

1.IO异常处理

文件不存在或打开失败

if (!(infile.is_open()))
{
    cout << "open file fail" << endl;
    exit(0);
}

文件读取异常或N值大于实际几何对象数目

if (!getline(infile, getLine))
{
    cout << "n is bigger than the true number of geometry or IO error" << endl;
    break;
}

N值小于实际几何对象数目

// 循环结束以后
if (getLine(infile, getLine))
{
	cout << "n is smaller than the true number of geometry" << endl;
    break;
}

2.输入合法性处理

N值合法性处理

regex reg1("\\s*\\+?0*[1-9]\\d*\\s*");  // 利用正则检查N输入的合法性:形式和范围
if (!regex_match(getLine, reg1))
{
    cout << "Error: the first line of input file is not a regular positive number." << endl;
}

几何对象格式化字符串合法性处理

用正则从形式和数值范围上约束几何对象格式化字符串

static string number = "([1-9]\\d{0,4})";
static string radius = "(\\+?" + number + ")";
static string number0 = "((0)|(" + number + "))";
static string snumber0 = "([+-]?" + number0 + ")";
static regex reg(
    "\\s*("
    "([LRS]\\s+" + snumber0 + "\\s+" + snumber0 + "\\s+" + snumber0 + "\\s+" + snumber0 + ")"
    "|"
    "(C\\s+" + snumber0 + "\\s+" + snumber0 + "\\s+" + radius + ")"
    ")\\s*"
);

if (!regex_match(erase, reg)) {
    throw domain_error(erase);
}

3.几何对象容器异常处理

重复几何对象输入处理

注意,咱们认为有无穷多交点但不重复的状况并不算作异常,只是在计算时不会考虑这种状况。

if (lineSet.count(&l) > 0)
{
    throw Doublication(l.toString());
}
if (circleSet.count(circle) > 0)
{
    throw Doublication(circle.toString());
}

定义点重合处理

if (start == end)
{
    throw pointDoublication(this->toString());
}

8、界面设计

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

  • 界面模块使用VS的mfc库进行设计,由于做业要求的界面并不复杂,因此使用不带菜单栏的对话框做为设计的基础。微软的官方mfc文档是设计时主要学习和参考的对象。最终的界面以下:

  • 从文件导入图形

    界面第一个Import按钮实现从文件导入的功能,经过用户输入读取的值得到文件路径,再经过接口与由计算模块实现具体的解析操做。在用户点击按钮时更新编辑框的控件变量,将获得的文本做为输入参数调用接口便可。

    UpdateData(TRUE);
    std::string path;
    path = CT2A(m_FILEPATH.GetString());
    try
    {
    	readFile(path);
    }
    catch (const std::exception&)
    {
    	m_EXCEPTMESSAGE = CString(_T("读取文件出现错误,请重启程序"));
    	UpdateData(FALSE);
    	return;
    }
    m_EXCEPTMESSAGE = CString(_T("读取文件成功"));
    UpdateData(FALSE);
  • 添加,删除图形

    经过ADDDELETE按钮能够进行图形的手工添加和删除,只需按照规定格式输入图形数据便可。

    在点击按钮后获取编辑框的控件变量,输入计算模块。以添加直线为例,具体代码为

    UpdateData(TRUE);
    std::string line;
    line = CT2A(m_LineValue.GetString());
    try
    {
    	addGeometryObject(line);
    }
    catch (const std::exception&)
    {
    	m_EXCEPTMESSAGE = CString(_T("添加图形时出现错误,请重启程序"));
    	UpdateData(FALSE);
    	return;
    }
  • 求解与绘制

    经过Solve按钮能够计算图形的交点个数,此功能也是经过调用计算模块实现的。在得到交点个数后反向更新控件变量便可显示数字。

    经过Draw按钮能够绘制现有图形的图像,默认是以网格坐标轴为背景显示图形。这部分先从计算模块获取图形信息,再调用相关的图形API完成绘图。为了解决坐标与像素大小转换的问题,在程序中设置了一个比例尺,用来调整坐标与像素的对应关系。具体的,在获得图形属性后,经过如下方式调整图形大小,resize即比例尺:

    circle(xc * resize, yc * resize, r1 * resize)
  • 调整坐标比例

    因为做业数据上限较大,因此默认的窗口大小可能不能显示完整的图形,经过两个按钮scale+scale-来调整内部的比例尺,以达到图片缩放的效果。每次点击这两个按钮都会改变比例尺。下图是具体的比例尺增大的效果。

9、先后端对接

在对接部分最重要的是接口的设计,良好的接口设计能够避免过多的操做,从而节省资源,提升性能。本做业的接口声明为

// parameter = file_name
__declspec(dllexport) void readFile(string);

// add from standard string fromat
__declspec(dllexport) void addGeometryObject(string);

// remove by standard string format, strictly equals
__declspec(dllexport) void removeGeometryObject(string);

// trigger calculation
__declspec(dllexport) pair<vector<string>, vector<Point>> getResult();

因为设计接口时有比较好的协商,界面模块与计算模块的对接只须要将数据按照约定的格式调用相关接口便可。固然,在运行前须要把dll文件和h文件添加到项目的包含目录中。生成与导入dll文件的步骤能够参考vs的官方文档。具体的说,导入文件,添加图形均可以通过相似的过程完成,以添加图形为例,大体的对接过程是:

UpdateData(TRUE);//更新输入数据
std::string line;
line = CT2A(m_LineValue.GetString());//由输入获取数据
try
{
	addGeometryObject(line);//调用接口
}
catch (const std::exception&)
{
	//异常处理,此部分较繁琐,以伪代码略去
}

绘图部分须要进行图形解析,因为商议的接口返回的是图形的几何,因此此部分对数据进行了分类和处理,再经过绘图方法绘制图形。主要逻辑是:

stringstream ss("");
string str = result;
ss.clear();
ss << str;
char type;
ss >> type;
switch (type)
{
case 'C': {
    setcolor(BLUE);
    double xc, yc, r1;
    ss >> xc >> yc >> r1;
    circle(xc * resize, yc * -resize, r1 * resize);//resize为上文所说的比例尺
    break;
    //绘图部分以调用API为主,步骤相似,为了博客的可读性其他绘图部分省略了
    }
case 'L': {//绘图}
case 'R': {//绘图}
case 'S': {//绘图}
default:{
	break;
}
}

10、结对过程

在前期的开发过程当中咱们是按照Pair Work的方法进行的,即一我的做为驾驶员控制键盘输入,另外一我的是领航员,起到领航,提醒的做用。而在后期的测试环节咱们经过互相交流,使测试更快速全面。如下是使用腾讯会议屏幕共享和交流的截图。

如下是交换使用github管理项目的截图

11、结对编程感触

1.结对优缺点

新冠肺炎席卷全世界,大三下学期的开头也在家中度过。往常结对编程是对面的进行,双方能一般快捷的交流;但今年的结对编程是在线上进行,隔着屏幕交流给人不天然的感受。

在特殊的结对编程条件下,我对结对编程的优缺点有了必定的认识。

优势

  • 提升效率:领航员能够给驾驶员压力,持续的处于高负荷状态,项目的进度很是快。
  • 减小bug:领航员能在编码以外进行考虑,从而能发现驾驶员没法发现的bug,领航员发现的的bug主要集中于:手误、判断条件错误、冗余代码、记忆不清致使的bug。
  • 互相学习:在结对编程的过程当中能窥见他人的思惟方式,从而帮助本身跳出思惟盲区,从新审视往常的编程习惯和准测。不合理的习惯能够改掉,而一直坚守的准测也能更加明晰具体的适用范围。

缺点

  • 磨合期带来很大的沟通成本:有时须要停下来用文字辅助才能明白对方确切表达的意思,由于很不熟悉对方的说话方式。
  • 不是全部时候都高效:当遇到双方都不会的问题时,结对编程演变成领航员和驾驶员一块儿用搜索引擎搜索的过程。尤为是遇到编译问题时,更是让人摸不着头脑,效率十分低。
  • 同步问题:寻找共同的线上结对编程实践比较困难,会有你等我、我等你的状况出现。

2.我与队友

队友
优势 开发高效、软件设计能力强、对项目总体节奏有所把控 特别积极、思路灵活变通、能学习钻研新方法新技术
缺点 学习新技术(GUI)时容易急躁 软件设计意识不强

12、附加做业 模块之间的松耦合

1.结果展现

与咱们合做的小组是 杜林峰 17373067 & 诸子钰 16021142

由于商议好了接口的规格,在替换接口时只须要更改VS的连接库设置便可。运行截图:

在运行中由于异常的处理不一样,会出现异常未捕获的状况,在修改catch语句后可正常运行。由于计算部分的结构已经设计好了,因此没有更改核心模块。

2. 合并过程

商议接口

由于双方软件的差异很大,因此须要商议一个接口做为桥梁。咱们商议出的接口以下:

#pragma once
#include <vector>
#include <iostream>

using namespace std;
typedef pair<double, double> InterfacePoint;

// parameter = file_name
__declspec(dllexport) void readFile(string);

// add from standard string fromat of geometry component
__declspec(dllexport) void addGeometryObject(string);

// remove by standard string format  of geometry component, strictly equals
__declspec(dllexport) void removeGeometryObject(string);

// trigger calculation
__declspec(dllexport) pair<vector<string>, vector<InterfacePoint>> getResult();

适配接口

编写新的模块适配接口,从而使先后端可以汇合。适配接口的代码以下

#include "Interface.h"
#include "read_file.h"
#include "table.h"

Table table;
// 适配读文件接口
void readFile(string inFilePath)
{
    ifstream infile = ifstream(inFilePath);
    readFromFile(table, infile);
    infile.close();
}
// 适配添加几何对象接口
void addGeometryObject(string geometryString)
{
    table.insertFromString(geometryString);
}
// 适配删除几何对象接口
void removeGeometryObject(string geometryString)
{
    table.eraseFromString(geometryString);
}
// 适配获得最终结果接口
pair<vector<string>, vector<InterfacePoint>> getResult()
{
    vector<string> geometries = vector<string>(table.getLineSet().size() + table.getCircleSet().size());
    vector<InterfacePoint> points = vector<InterfacePoint>(table.getPointNum());
    geometries.clear();
    points.clear();
    for (auto i : table.getLineSet())
    {
        geometries.push_back(i->toString());
    }
    for (auto i : table.getCircleSet())
    {
        geometries.push_back(i.toString());
    }
    for (auto i : table.getPointSet())
    {
        points.push_back(InterfacePoint(i.pointX, i.pointY));
    }
    return pair<vector<string>, vector<InterfacePoint>>(geometries, points);
}

生成DLL

在接口适配完成后,生成后端DLL库。

两组交换对接

该步骤比较顺利一次完成。

相关文章
相关标签/搜索