BUAA软件工程结对项目作业
BUAA软件工程结对项目
小组成员:16005001,17373192
1、教学班级和项目地址
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 博客园班级连接 |
| 这个作业的要求在哪里 | 结对项目作业 |
| 我在这个课程的目标是 | 提高软件开发能力、团队协作能力 |
| 这个作业在哪个具体方面帮助我实现目标 | 感受结对开发 |
| 项目代码 | https://github.com/monokuma-zhuo/Intersect |
| 教学班级 | 006 |
2、PSP表格
| SP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | ||
| · Estimate | · 估计这个任务需要多少时间 | 30 | 30 |
| Development | 开发 | ||
| · Analysis | · 需求分析 (包括学习新技术) | 100 | 180 |
| · Design Spec | · 生成设计文档 | 20 | 30 |
| · Design Review | · 设计复审 (和同事审核设计文档) | 20 | 20 |
| · Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 5 | 5 |
| · Design | · 具体设计 | 40 | 60 |
| · Coding | · 具体编码 | 180 | 200 |
| · Code Review | · 代码复审 | 30 | 60 |
| · Test | · 测试(自我测试,修改代码,提交修改) | 60 | 100 |
| Reporting | 报告 | ||
| · Test Report | · 测试报告 | 10 | 10 |
| · Size Measurement | · 计算工作量 | 10 | 10 |
| · Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
| 合计 | 535 | 735 |
3、看教科书和其它资料中关于 Information Hiding,Interface Design,Loose Coupling 的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的
接口设计时主要是减少接口之间的耦合度,确保每个接口都只完成各自的一项功能,避免出现冗杂,从而实现松耦合。核心模块和ui之间的接口设计就是这样实现的,从ui程序的角度看我只需要知道接口名字,拿到接口返回的数据即可,而不需要关注接口内部是如何设计的。在类中没有写私有属性,但是封装成dll文件后供ui使用本身也是一种信息隐藏,因为ui程序并不能知道接口的源码设计,只通过接口名字调用即可。
4、计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处
本次作业共有直线类,射线类,线段类,圆类,函数除了每个类中求交点的方法外,还写了几个供ui调用的接口函数。
几何对象的表示
上次作业中,由于直线与圆无明显的联系,故我采用结构体来表示,但本次作业新增了射线与线段,射线与线段明显与直线有联系,可以看作是特殊的有界的直线,故采用继承的方式。分别建立直线类与圆类,射线类与线段类继承直线类。对于交点的存储,本次作业中采用pair<double,double>的形式。
对于几何对象,采用vector进行存储,交点采用set进行存储。
交点的求解
参考上次作业的求解方法,在本次作业中依旧使用类似的方法,在类中编写与其他几何对象计算交点的方法。
在计算交点之前,还需要判断交点是否存在。判断直线与直线是否平行的方法,依旧采取上次作业中提到的方法,比较$A1B2$与$A2B1$是否一致。直线与圆则采取圆心到直线的距离与半径的关系,圆与圆采取比较圆心距离与两圆半径之间的关系。线段与射线当作直线来考虑。考虑到double的精度问题,在判断相等时采取$fabs(a-b)<1e-10$的方式进行判断。
求解交点的方式依旧采取上次作业的方式,关于线段与射线,采取上次作业所述求解方法得到交点后,还需判断交点是否在线段或射线上,具体做法为对求解出的点进行范围判断,同时还需要考虑到斜率不存在的情形。由于线段与射线与其他图形求解得到的结果可能不存在,故在返回值中增加标记位用来描述此解是否存在。
值得注意的是,线段与射线在共线的情况下依旧可能有解,这是与直线有着很大的区别,故需要对线段、射线的判断平行函数进行重写,维护一个全局变量用于存储这个可能存在的解。
交点的去重
采用set进行去重,重写了存储交点的pair<double,double>的operator<的方法来保证去重的正确性,同时还考虑到了精度问题,在重写<的时候均使用$fabs(a-b)<1e-10$先判断是否等于关系成立。
5、阅读有关 UML 的内容。画出 UML 图显示计算模块部分各个实体之间的关系(画一个图即可)。
如图由VS生成的uml图

6、计算模块接口部分的性能改进。记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展示一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展示你程序中消耗最大的函数。
采用10000个随机生成的几何图形得到以下结果。

程序中消耗最大的为set的去重工作。曾经考虑过使用unordered_set来代替set,但是在换成unordered_set编写hash函数时遇到了有效数字以及精度问题和冲突问题,最终还是决定使用set来保证正确性。
7、看 Design by Contract,Code Contract 的内容描述。这些做法的优缺点,说明你是如何把它们融入结对作业中的
DBC要求使用者和被调用者地位平等,双方必须彼此履行义务。
优点:1、减少因自然语言的歧义等带来的不必要麻烦。
2、保证了双方代码的质量。
3、提高了软件工程的效率和质量 。
缺点:1、对程序语言有一定要求,需要支持断言。
2、写起来比较繁琐,可能会带来不必要的麻烦。
8、计算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到 90% 以上,否则单元测试部分视作无效。
对于程序正确性、各个方法的正确性,斜率不存在等特殊情况以及各种异常进行了测试。
这是其中一个测试正确性的函数
TEST_METHOD(TestCorrectness)
{
set<pair<double, double>> Array_dot;
Line l1 = Line(-1, 4, 5, 2);
Cycle c1 = Cycle(3, 3, 3);
Ray r1 = Ray(2, 5, -1, 2);
Segment s1 = Segment(2, 4, 3, 2);
pair<pair<double, double>, int> dot1 = l1.intersect(r1);
if (dot1.second == 1) {
Array_dot.insert(dot1.first);
}
dot1 = l1.intersect(s1);
if (dot1.second == 1) {
Array_dot.insert(dot1.first);
}
pair<pair<double, double>, pair<double, double>> dot2 = c1.line_cycle_instere(l1);
Array_dot.insert(dot2.first);
Array_dot.insert(dot2.second);
pair<pair<pair<double, double>, pair<double, double>>, pair<int, int>> dot3 = c1.ray_cycle_instere(r1);
if (dot3.second.first == 1) {
Array_dot.insert(dot3.first.first);
}
if (dot3.second.second == 1) {
Array_dot.insert(dot3.first.second);
}
dot1 = r1.intersect(s1);
if (dot1.second == 1) {
if (r1.x1 < r1.x2) {
if (r1.x1 <= dot1.first.first) {
Array_dot.insert(dot1.first);
}
}
else if (r1.x1 > r1.x2) {
if (r1.x1 >= dot1.first.first) {
Array_dot.insert(dot1.first);
}
}
else {
if (r1.y1 > r1.y2) {
if (r1.y1 >= dot1.first.second) {
Array_dot.insert(dot1.first);
}
}
else if (r1.y1 < r1.y2) {
if (r1.y1 <= dot1.first.second) {
Array_dot.insert(dot1.first);
}
}
}
}
Assert::AreEqual(5, (int)Array_dot.size());
}
这是斜率不存在的测试
TEST_METHOD(TestAequals0)
{
Line l1 = Line(0, 0, 1, 1);
Ray r1 = Ray(1, 0, 2, 0);
pair<pair<double, double>, int> dot1 = l1.intersect(r1);
Assert::AreEqual(0, (int)dot1.first.first);
Assert::AreEqual(0, (int)dot1.first.second);
Assert::AreEqual(0, (int)dot1.second);
}
对各种异常的测试采取读入预先写好的存在错误的文件的方式
TEST_METHOD(TestOperationException) {
auto func = [] {test("input1.txt"); };
Assert::ExpectException<OperatorException>(func);
/*try {
test("input1.txt");
}
catch (OperatorException& e) {
Assert::AreEqual("Operation Exception", e.what());
}*/
}
TEST_METHOD(TestEndException) {
auto func = [] {test("input2.txt"); };
Assert::ExpectException<EndException>(func);
/*try {
test("input2.txt");
}
catch (EndException& e) {
Assert::AreEqual("End Exception", e.what());
}*/
}
TEST_METHOD(TestDefectException) {
auto func = [] {test("input3.txt"); };
Assert::ExpectException<DefectException>(func);
/*try {
test("input3.txt");
}
catch (DefectException& e) {
Assert::AreEqual("Defect Exception", e.what());
}*/
}
TEST_METHOD(TestNumberException) {
auto func = [] {test("input4.txt"); };
Assert::ExpectException<NumberException>(func);
/*try {
test("input4.txt");
}
catch (NumberException& e) {
Assert::AreEqual("Number Exception", e.what());
}*/
}
代码覆盖率

单元测试

9、计算模块部分异常处理说明。在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。
设计了如下异常
| 错误类型 | 输入(其中一种) | 描述 |
|---|---|---|
| 操作符错误 | 2 L 0 0 1 1 Z 0 0 -1 -1 | 操作符出入错误 |
| 有多余字符 | 2 C 1 1 1 C 1 1 1 L 1 1 1 0 | 几何对象超出数目 |
| 缺少字符 | 2 L 1 1 1 1 | 缺少几何对象或数字 |
| 数字格式错误 | 1 L 10000 1000a 1 1 | 数字格式错误 |
| 数字越解 | 1 L 10000000 1 1 0 | 输入数字超过100000范围 |
| 直线重复 | 2 L 1 1 0 0 L 0 0 1 1 | 直线出现重合导致无数交点,射线与线段的无数交点情形也算作此异常 |
| 圆重复 | 2 C 1 1 1 C 1 1 1 | 圆重复导致无数交点 |
| 直线非法 | 1 L 1 1 1 1 | 直线的两个端点相同构不成直线 |
| 圆非法 | 1 C 1 1 0 | 圆的半径非法 |
异常均会在控制台输出并且同时输出行号方便定位
10、界面模块的详细设计过程。在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。
界面模块我们使用Qt进行编写,由于Qt的窗格是以左上角为原点,向下为y轴正方向,所以需要重新绘制一个坐标系。我们圈了600*600区域为坐标系,同时绘制了x轴和y轴。之后设置了4个按钮,分别为添加图形、从文件中添加、删除、求交点。
添加图形时会弹出一个新窗格来输入图形类型和点坐标,确定后进行绘制,并调用core模块的接口函数返回当前交点集合并存储。从文件中添加也是同理,只不过输入的数据是从文件中读取。
信息发送与接收的部分代码
void MainWindow::on_pushButton_clicked()
{
dialog1=new Dialog;
connect(dialog1,SIGNAL(sendData(int,QString,QString,QString,QString,QString)),this,SLOT(receiveData(int,QString,QString,QString,QString,QString)));
dialog1->show();
}
void MainWindow::receiveData(int type,QString name,QString x1,QString y1,QString x2,QString y2)
{
if(type==0){
Paint_Line(x1.toDouble(),y1.toDouble(),x2.toDouble(),y2.toDouble());
}
else if(type==1){
Paint_Ray(x1.toDouble(),y1.toDouble(),x2.toDouble(),y2.toDouble());
}
else if(type==2){
Paint_Segment(x1.toDouble(),y1.toDouble(),x2.toDouble(),y2.toDouble());
}
else if(type==3){
Paint_Cycle(x1.toDouble(),y1.toDouble(),x2.toDouble());
}
}
绘制直线的代码
void MainWindow::Paint_Line(double x1,double y1,double x2,double y2)
{
intersect_point=input_line(std::make_pair(std::make_pair(x1,y1),std::make_pair(x2,y2)));
QPainter painter(&image);
QPen pen;
pen.setColor(Qt::blue);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing, true);
if(x1==x2)
{
painter.drawLine(QPointF(pointx0+x1,0),QPointF(pointx0+x2,600));
}
else if(y1==y2)
{
painter.drawLine(QPointF(0,pointy0-y1),QPointF(600,pointy0-y2));
}
else{
double k=(y2-y1)/(x2-x1);
double b=y1-k*x1;
painter.drawLine(QPointF(0,-1*(-300*k+b)+pointy0),QPointF(600,-1*(300*k+b)+pointy0));
}
}
从文件读取的代码
void MainWindow::on_pushButton_3_clicked()
{
QString filename;
filename=QFileDialog::getOpenFileName(this,tr("文件"),"",tr("text(*.txt)"));
if(!filename.isNull())
{
QFile file(filename);
if(!file.open(QFile::ReadOnly|QFile::Text))
{
QMessageBox::warning(this,tr("error"),tr("read file error:&1").arg(file.errorString()));
return;
}
QTextStream in(&file);
while(!in.atEnd())
{
QString line=in.readLine();
QList<QString> list;
list=line.split(' ');
if(list[0]=="L")
{
Paint_Line(list[1].toDouble(),list[2].toDouble(),list[3].toDouble(),list[4].toDouble());
}
else if(list[0]=="R")
{
Paint_Ray(list[1].toDouble(),list[2].toDouble(),list[3].toDouble(),list[4].toDouble());
}
else if(list[0]=="S")
{
Paint_Segment(list[1].toDouble(),list[2].toDouble(),list[3].toDouble(),list[4].toDouble());
}
else if(list[0]=="C")
{
Paint_Cycle(list[1].toDouble(),list[2].toDouble(),list[3].toDouble());
}
}
}
}
删除时需要清空当前坐标系中的几何图形和交点坐,并调用core模块中的接口函数清除core中已有的几何图形。
void MainWindow::on_pushButton_2_clicked()
{
QList<QLabel*> array_label=this->findChildren<QLabel *>();
for(int i=0;i<array_label.size();i++)
{
array_label[i]->clear();
}
delete_all();
image.fill(Qt::white);
Paint();
update();
}
求交点时,直接根据存储的坐标信息在图上setText即可。
void MainWindow::on_pushButton_4_clicked()
{
std::set<std::pair<double, double>>::iterator it;
for(it=intersect_point.begin();it!=intersect_point.end();it++)
{
QString x=QString::number((*it).first,'f',1);
QString y=QString::number((*it).second,'f',1);
QLabel *text=new QLabel(this);
text->setText("("+x+","+y+")");
text->setGeometry(300+(*it).first,300-(*it).second,100,25);
text->show();
}
}
11、界面模块与计算模块的对接。详细地描述 UI 模块的设计与两个模块的对接,并在博客中截图实现的功能。
如下为core模块中的接口函数
IMPORT_DLL std::set<std::pair<double,double>> solve(std::vector<std::pair<char, std::pair<std::pair<int, int>, std::pair<int, int>>>> line, std::vector<std::pair<char, std::pair<std::pair<int, int>, int>>> circle);
IMPORT_DLL bool if_line_same(std::pair<std::pair<double, double>, std::pair<double, double>> a, std::pair<std::pair<double, double>, std::pair<double, double>> b);
IMPORT_DLL bool if_circle_same(std::pair<std::pair<double, double>, double> c1, std::pair<std::pair<double, double>, double> c2);
IMPORT_DLL std::set<std::pair<double, double>> input_line(std::pair<std::pair<double, double>, std::pair<double, double>> line1);
IMPORT_DLL std::set<std::pair<double, double>> input_ray(std::pair<std::pair<double, double>, std::pair<double, double>> ray1);
IMPORT_DLL std::set<std::pair<double, double>> input_segment(std::pair<std::pair<double, double>, std::pair<double, double>> segment1);
IMPORT_DLL std::set<std::pair<double, double>> input_circle(std::pair<std::pair<double, double>, double> circle1);
IMPORT_DLL long main1(int argc, char* argv[]);
IMPORT_DLL void delete_all();
在Qt的.pro工程中加入如下两行代码
LIBS+=D:/softwareproject/Gui3/untitled/core.lib
INCLUDEPATH +=D:/softwareproject/Gui3/untitled/pch
并将core.dll文件放在和intersect.exe同一目录下,即可在Qt工程中正常调用接口函数。
如图为实现的添加图形,显示坐标等功能

12、描述结对的过程,提供两人在讨论的结对图像资料(比如 Live Share 的截图)。


13、看教科书和其它参考书,网站中关于结对编程的章节,说明结对编程的优点和缺点。同时描述结对的每一个人的优点和缺点在哪里(要列出至少三个优点和一个缺点)。
结对编程:
优点:1、两人复查代码,减少bug。
2、可以互相学习对方的长处,比如一些编程小技巧,代码规范等
缺点:1、受时空影响,比如现在各自在家交流有时不方便
2、商讨可能会占据大量时间导致效率降低
我:
优点:1、能够比较快的理解问题,并想出一些解决方案
2、写代码效率还可以
3、能够比较快的学习新知识并应用
缺点:1、代码风格不好,队友读起来比较吃力。
队友
优点:1、做事认真负责
2、能够清楚的表达出自己的想法,交流比较容易
3、找bug能力强,能发现一些被忽视的bug
缺点:1、代码架构方面写的不是很好,会出现代码冗杂的情况
附上无警告的截图

附加题:松耦合
合作组同学学号:17373167,17373349
如下图为我们组的ui运行他们组的core模块的修改与结果:


由于我们两组预先并没有商量好统一的接口名字,导致并不能无任何修改的使用对方的core模块,所以在ui上我们需要修改文件名和接口名。并且由于返回的数据类型也不一样,所以需要新建一个合适的容器来存储他们的接口函数返回的交点坐标。
BUAA软件工程结对项目作业的更多相关文章
- BUAA软件工程个人项目作业
BUAA软件工程个人项目作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 学习软件开发的流程 这个作业在哪 ...
- BUAA 2020 软件工程 结对项目作业
Author: 17373051 郭骏 3.28添加:4.计算模块接口的设计与实现过程部分,PairCore实现的细节 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) ...
- BUAA 软工 结对项目作业
1.相关信息 Q A 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结对项目作业 我在这个课程的目标是 系统地学习软件工程开发知识,掌握相关流程和技术,提升 ...
- BUAA软工-结对项目作业
结对项目作业 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 结对项目作业 我在这个课程的目标是 通过这门课锻炼软件开发能力和经验,强化与他人合作 ...
- BUAA 2020 软件工程 个人项目作业
BUAA 2020 软件工程 个人项目作业 Author: 17373051 郭骏 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 ...
- 11061160_11061151_Pair Project: Elevator Scheduler软件工程结对编程作业总结
软件工程结对编程作业总结 11061160 顾泽鹏 11061151 庞梦劼 一.关于结对编程 这次的软工任务既不是单打独斗的个人任务,也不是集思广益的团队项目,而是人数为两人的结对编程.两个人合 ...
- 2021S软件工程——结对项目第一阶段
# 2021S软件工程--结对项目第一阶段 2021春季软件工程(罗杰 任健) 项目地址 1020 1169 --- ## 1 结对感受 总体来说,结对编程与之前的个人编程感觉有很大的不同.有如下几个 ...
- 2021S软件工程——结对项目第三阶段
2021S软件工程--结对项目第三阶段 2021春季软件工程(罗杰 任健) 项目地址 1020 1169 1 实践反思 1.1 问题分析 两人习惯不一致 没有具体制定时间节点 写完代码才开始" ...
- 结对项目作业GUI
一.Coding.Net项目地址:https://git.coding.net/zhengsh589/CoupleProject.git 二.PSP表格(完成前): PSP 任务内容 计划共完成需要的 ...
随机推荐
- Linux上使用设置printf显示的颜色
我们经常看到别的屏幕五颜六色的很是羡慕,看着很炫是吧.其实我们也可以自己做一个简单的修改,是我们的显示结果也呈现出不同的颜色.shell脚本可能设置的比较多,但是我们平常使用C语言却很少设置它的颜色, ...
- pyRevit开发:如何创建轴网
必看部分: Document获取: 必看文章 如何添加基本项目引用 基础部分: 创建轴网 基本思路: 首先添加引用 获取当前项目文档 创建轴网定位线 创建轴网 设置轴网名称 实现代码: import ...
- Docker安装mysql镜像并进行主从配置
Docker安装mysql镜像并进行主从配置 1.下载需要的mysql版本镜像 docker pull mysql:5.6 2.启动mysql服务实例(基本启动) #启动主mysql docker r ...
- Tars | 第2篇 TarsJava SpingBoot启动与负载均衡源码初探
目录 前言 1. Tars客户端启动 @EnableTarsServer 2. Communicator通信器 3. 客户端的负载均衡调用器LoadBalance 最后 前言 通过源码分析可以得出这样 ...
- PHP设计模式之状态模式
状态模式从字面上其实并不是很好理解.这里的状态是什么意思呢?保存状态?那不就是备忘录模式了.其实,这里的状态是类的状态,通过改变类的某个状态,让这个类感觉像是换了一个类一样.说起来有点拗口吧,先学习概 ...
- 创建一个新的解耦的Orchard Core CMS网站
引言本文将介绍创建一个功能齐全.解耦的CMS网站的过程,该网站允许您编辑博客帖子并呈现它们.解耦是一种开发模型,其中站点的前端和后端(管理)托管在同一个Web应用程序中,但只有后端由CMS驱动.然后, ...
- Shell系列(27)- 条件判断之两个整数比较
两个整数之间比较 Liunx中,都是字符型,但是加了数值比较的选项,所以自动将他们转换成了整数型进行比较,不需要对这些参数进行变量转换或者重新声明 测试选项 作用 整数1 -eq 整数2 判断整数1是 ...
- 安卓模拟器genymotion安装
上一篇已经讲了appium的搭建.那么搭建好后,我们需要测试不同机型,这个时候除了真机外,可以选择安装模拟器.市面上的模拟器有很多:夜神.逍遥.mumu.android emulator.genymo ...
- mac使用brew update无反应,更新慢解决办法
一.原因 主要是资源访问太慢的原因造成的,替换一下镜像就可以了 有点耐心,大概5分钟就可以了,刚开始的时候terminal 只有顶部的title栏会变化,最后才会出现更新结果 使用中科大的镜像 替换默 ...
- windows terminal+wsl+neovim配置过程杂记
长期记录,草稿 coc依赖于node,直接sudo apt intsll node得到的版本是10.x,无法满足要求, 这篇博客介绍了安装新版node的方法https://www.cnblogs.co ...