github连接 html
项目 | 内容 |
---|---|
这个做业属于哪一个课程 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2020_LJ |
这个做业的要求在哪里 | https://edu.cnblogs.com/campus/buaa/BUAA_SE_2020_LJ/homework/10466) |
我在这个课程的目标是 | 提高软件开发能力 |
这个做业在哪一个具体方面帮助我实现目标 | 提高了我的编程能力, 测试和提高代码性能的能力 |
PSP2.1 | Personal SoftWare Process Stages | 预计耗时 | 实际耗时 |
---|---|---|---|
Planning | 计划 | 120 | 60 |
Estimte | 估计这个任务须要多少时间 | 1800 | 1700 |
Development | 开发 | 1100 | 1000 |
Analysis | 需求分析 | 0 | 0 |
Desing Spaec | 生成设计文档 | 100 | 40 |
Design Review | 设计复审 | 100 | 10 |
Coding Standard | 代码规范 | 0 | 0 |
Design | 具体设计 | 60 | 40 |
Coding | 具体编码 | 1100 | 180 |
Code Review | 代码复审 | 60 | 60 |
Test | 测试 | 180 | 180 |
Reporting | 报告 | 100 | 120 |
Test Report | 测试报告 | 0 | 0 |
Size Measurement | 计算工做量 | 5 | 0 |
Postmortem & Process Improvement | 过后总结 | 60 | 50 |
- | 合计 | 1800 | 1700 |
通过查阅资料和学习, 我了解到在接口设计中有经常使用的一些原则, 本次做业的设计中, 咱们用到了这些原则。node
单一职责原则c++
Single Responsibility Principle: There shuld never be more than one reason for class to changegit
单一职责原则要求一个接口, 一个功能, 若是在private 方法和public接口中, 改动过多, 那么容易引发错误。 在结对编程的设计中, 计算模块里咱们尽可能保证一个函数只作一个事情, 好比Line Circle Point分出三个类, 每一个求交点的函数只处理一种类型等。github
里氏替换原则算法
Liskov Substitution Principle: Functions that use pointers or references to base classed must be able to use objecsts of derived classes without knowing it编程
这一法则是多态的体现, 全部父指针的使用点均可以用子指针, 这要求了子类只扩展父类, 可是不能够用在覆写父类函数的时候让函数的应用范围减小。 在最初的版本中, 咱们采用了Line Circle继承父类的方式实现, 后来发现这种继承影响性能, 就放弃了继承。windows
依赖倒置原则数据结构
依赖倒转原则是TDD测试驱动开发的体现, 要求高层模块不依赖特定的底层模块, 而是依赖其抽象, 这要求编程者提早想好接口。架构
接口隔离原则
clients should not be forced to depend upon interfacess that they don't use
保证接口单一, 不要过于臃肿, 在计算模块和UI模块的对接时, 咱们尽可能细化接口, 让模块的每一个按钮对应了恰好一个接口, 同时接口之间的功能达到了隔离。
迪米特法则
law of Demeter: least knowledge
loose coupling: Loose coupling means they mostly independent
这一原则要求两个类之间要低耦合, 达到loose coupling, 一个类不须要知道另外一个类是怎么实现的, 更不须要由于一个类内部代码的修改二更改本身的方法。 在咱们的结对编程中, 为了实现接口的高效对接, 对于接口进行和抽象和封装
共设置4个类,分别为表示直线、射线、线段的Line
类(经过成员m_type
区分),表示圆的Circle
类,表示交点的Node
类,以及负责维护几何对象、计算交点及输出结果的主类Intersect
类。几何对象类的成员包含构造参数以及为求解交点时减小计算量而预存的衍生量。此外,Line
类还包含一个判断输入的交点是否在该线上的online
成员函数。
主类Intersect
在实例化后能够经过addGeoFromFile
函数从目标路径文件中读入几何对象,也能够经过addGeoByString
函数由指定格式的字符串添加几何对象。还能够经过removeGeoByString
函数删除容器中和指定格式的字符串所表示的几何对象相同的几何对象。主类提供CalculateIntersections
函数用于触发交点的计算,这个函数经过调用相应的交点计算函数计算每两个几何体所造成的交点并保存在容器中。在交点计算完毕后能够经过GetIntersectionNumber
函数获得交点个数,或使用GetGeoObjects
和GetIntersections
函数输出几何对象的具体信息,也可经过ViewIntersections
函数打印交点至标准输出以快速调试。
主类在实例化时生成存放几何对象及交点的容器,并在后续过程当中不断更新。UML图以下:
交点的计算根据代数或几何方法实现。其中,线间交点的求解依赖行列式,较为工整规范;线圆交点的求解经过将圆心平移至原点,简洁地计算出交点后再平移回去的方法实现(可视为坐标系的线性变换);圆圆的交点则经过相交部分的三角关系求解。具体以下:
对于\(L_1\{(x_1,y_1), (x_2,y_2)\},L_2\{(x_3,y_3),(x_4,y_4)\}\),交点为:
判据为\(\Delta= \left|\begin{array}{} x_1-x_2 & y_1-y_2\\ x_3-x_4 & y_3-y_4 \end{array}\right|\),有:
为减小计算,读入直线时预存\(x_1-x_2,y_1-y_2\)和\(\left|\begin{array}{}x_1 & x_2\\y_1 & y_2\end{array}\right|\)。
对于\(L\{(x_1,y_1),(x_2,y_2)\},C\{(x_c,y_c),r\}\),交点为:
其中:
判据为\(\Delta=r^2{d_r}^2-D^2\),有:
为减小计算,读入圆时预存\(r^2\)。
对于\(C_1\{(x_1,y_1),r_1\},C_2\{(x_2,y_2),r_2\}\),交点为:
其中:
判据为\(\Delta={r_1}^2-a^2\),有:
参考资料:
https://mathworld.wolfram.com/Line-LineIntersection.html
https://mathworld.wolfram.com/Circle-LineIntersection.html
http://paulbourke.net/geometry/circlesphere/
对射线和线段的支持,在不改变求交点算法的前提下实现。在求出交点后由Line
对象使用成员函数online
根据m_type
判断交点是否在该线上,决定是否将其加入交点集m_allIntersections
中。其中:
为节省计算,在构造线段时预存横纵坐标的最值。
特别地,须要额外考虑非直线的线型对象之间的相交。当其判据\(\Delta\)为零时(向量平行)仍可能相交。若数据合法,则只可能相交于端点处,所以予以特判。
将计算模块封装为动态链接库后,对外接口有三。一为readFile
函数,用于从文件中输入几何对象;二为addGeometryObject
函数,用于经过字符串输入单个几何对象;三为getResult
函数,用于触发交点的计算并返回一个由包含全部几何对象描述串的列表和由全部交点构成的列表所组成的元组,形如pair<vector<string>, vector<Point>>
。
UML图是用来表示类之间设计关系的图表, 主要有如下五种关系
用UML图表示计算模块的关系以下图所示
一共用三个类来存数据, Line, Circle, Node(点), Intersect里面聚合了这三种数据, 进行一系列运算
记录在改进计算模块性能上所花费的时间,描述你改进的思路,并展现一张性能分析图(由VS 2015/2017的性能分析工具自动生成),并展现你程序中消耗最大的函数。(3')
经过随机生成的数据进行性能测试,占用CPU时间最多的子函数每每是与交点存储所用数据结构相关的函数,如选用unordered_set
时哈希值的计算,选用set
时元素之间的比较等。所以交点存储数据结构的选择对性能的提高十分关键,备选方案及可能的问题以下:
unordered_set
:须要考虑哈希桶的数量(根据几何对象数目肯定)和哈希函数的设计。set
:虽然红黑树的更新较哈希表慢,但不存在rehash的问题。vector
+sort
+unique
:重复的交点较多时会超过内存限制,须要随时检测去重。所以最终的问题在于在给定的数据条件下哪一种数据结构更优。通过比较,采用了unordered_set
,并在Intersect
类初始化时使用reserve
函数预设哈希桶数量为\(5000000\)以免rehash开销。在随机生成的\(2000\)个几何对象的测试中(交点数在1M以上)性能分析图以下:
可见,CPU占用率最高的函数为存放交点的unordered_set
调用的equal_to
函数。这说明哈希值的碰撞较多,能够考虑优化哈希值的计算。查阅资料后选取了较为合适的:
须要注意的是,浮点数的哈希会致使在精度范围内相等的数被哈希至不一样哈希桶,致使重复计算。针对这个问题将哈希函数修改成:
参考资料:
https://stackoverflow.com/questions/16792751/hashmap-for-2d3d-coordinates-i-e-vector-of-doubles
https://www.zhihu.com/question/52368555
经过查阅如下材料,修改了原始程序中可能致使性能降低的代码,改进包含:
new
来给指针分配堆空间,减少内存管理开销。emplace
系列函数向容器中插入元素,避免push
的拷贝开销。http://www.javashuo.com/article/p-hvmxpkiy-nn.html
https://blog.csdn.net/nodeman/article/details/80771789
https://blog.csdn.net/p942005405/article/details/84764104
Design By Contract: 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.
在上学期的OO中, 我了解到和约设计须要定义如下的条件
因为对于每一个函数用和约设计过于复杂, 并不实用, 所以咱们对于模块进行和约设计, 各自保证计算模块和UI模块的正确性, 独立测试。
展现出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试获得的测试覆盖率截图,发表在博客中。要求整体覆盖率到 90% 以上,不然单元测试部分视做无效。(6')
本次单元测试继承了上次的测试代码,放入OldTest
类中。其包含三个TEST_METHOD
,分别在仅有直线、仅有圆以及线圆混合的状况下针对几何对象之间的相离、相切、相交、共交点等状况各测试\(5\)个样例。其中测试直线的部分以下:
新增功能的测试则包含在NewTest
类中,其下辖三个TEST_METHOD
针对射线和线段之间的位置关系及特殊状况(平行交于端点)共测试了\(14\)个样例。其中测试线段的部分以下:
此外还设置了精度和溢出测试。精度损失来源于sqrt
操做;溢出是当坐标接近\(10^5\)时,\(\Delta=r^2{d_r}^2-D^2\)等判据可能超过long long
的表示范围,所以圆相关一切计算改用double
进行。
测试样例彻底覆盖了核心计算代码:
在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。(5')
异常类型 | 设计目标 | 样例 |
---|---|---|
CMDFormatException | 命令行输入错误 | ./intersect.exe -p xx |
FileNotFoundException | 输入文件不存在 | ./intersect.exe -i nowhere -o out.txt |
FileFromatException | 文件内容格式错误 | C L 1 1 -1 |
CoordinateLimitExceedException | 坐标超限 | 1 C 999999 0 1 |
LineDefinitionException | 两个参数点重合 | 1 L 1 1 1 1 |
CircleDefinitionException | 圆的半径小于等于零 | 1 C 0 0 -1 |
InfinateIntersectionException | 几何对象间存在重叠 | 2 R 0 1 0 2 S 0 3 0 4 |
由于以前没有GUI编程的经验, 在设计界面模块以前,我首先调查了windows下有哪些GUI开发的库可使用。
1.MFC
2.QT
3.WPF
4.WTL
在调查以后, 据网上评价QT比较好用, 并且是跨平台的, 可是由于安装文件过大, 电脑给windows的分区不足, 最终选择了VS自带的MFC。
MFC有两种设计方式, 一个是dialog app一个是document app, 我选择的是dialog app, 开发一个窗口。
使用visual studio mfc生成dialog app后, 在资源文件里能够找到界面对应的文件, 打开后以下图, 能够添加组件
我主要使用了三种组件
Button组件用来获取用户的点击操做, 每次点击会触发一个onclick函数
void CMFCApplication1Dlg::OnBnClickedButton1() { CString geomLine; edit_geo_obj.GetWindowTextW(geomLine); CT2CA sa(geomLine); std::string s(sa); addGeometryObject(s); }
函数内能够添加各类操做, 好比在个人界面中, 有如下几个按钮
输入文件
增长几何对象
删除几何对象
计算交点(而且绘制)
关闭
static text组件用来展现文字
Edit control是一个文本框, 能够用来输入文字, 每一个文本框会有一个变量, 用来获取文字。
好比file.GetWindowTextW(geomline)
就是把文件输入框中的文字读入到geomLine这个Ctring中
最后, 点击计算交点后, 会把从计算模块获得的交点绘制出来, 调用draw函数画在GUI的中央, 以下图所欧式
因为咱们的接口比较少, 在界面模块对接时很流畅。
获得intersectProjectDll.lib和Interface.h后, 我看到有三个接口
__declspec(dllexport) void readFile(string); __declspec(dllexport) void addGeometryObject(string); __declspec(dllexport) void removeGeometryObject(string); __declspec(dllexport) pair<vector<string>, vector<Point>> getResult();
这三个接口是咱们以前商定好的接口, 尽可能隐藏内部实现细节, 只暴露三个接口
功能以下图
图中有三个按钮, 输入文件调用readFile
, 添加几何对象调用addGeometryObject
, 计算交点调用getResult
而且绘制
实现以上功能后, 对接工做完成。
使用工具: 腾讯会议
具体过程:
结对编程的优势:
结对编程的缺点:
在结对编程的时候, 每个人的优缺点以下:
对驾驶员的优势
对驾驶员的缺点
对领航员的优势
对领航员的缺点
咱们互换的组是 http://www.javashuo.com/article/p-dumtxcfu-nn.html
咱们选择的组和咱们有类似的接口, 在替换核心模块的时候经历了一下几个步骤:
1.修改dll, lib路径, 成功引入头文件, 可以连接到库
2.修改输入文件, 添加几何对象, 计算交点的接口名称
3.运行
运行结果以下: