zoukankan      html  css  js  c++  java
  • 第一次结对项目

    第一次结对项目

    项目 内容
    这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健)
    这个作业的要求在哪里 结对项目作业
    我在这个课程的目标是 通过这门课强化软件开发能力,熟悉与他人合作的能力
    这个作业在哪个具体方面帮助我实现目标 使用结对编程的模式
    1. 在文章开头给出教学班级和可克隆的 Github 项目地址(例子如下)。(1')

    2. 在开始实现程序之前,在下述 PSP 表格记录下你估计将在程序的各个模块的开发上耗费的时间。(0.5')(独立完成)

      PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
      Planning 计划
      · Estimate · 估计这个任务需要多少时间 10 10
      Development 开发
      · Analysis · 需求分析 (包括学习新技术) 30 40
      · Design Spec · 生成设计文档 26 25
      · Design Review · 设计复审 (和同事审核设计文档) 10 9
      · Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
      · Design · 具体设计 20 21
      · Coding · 具体编码 480 500
      · Code Review · 代码复审 50 40
      · Test · 测试(自我测试,修改代码,提交修改) 50 30
      Reporting 报告
      · Test Report · 测试报告 60 55
      · Size Measurement · 计算工作量 20 30
      · Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 10
      合计 776 780
    3. 看教科书和其它资料中关于 Information Hiding,Interface Design,Loose Coupling 的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的。(5')(独立完成)

      1. Information Hiding

      ​ Information hiding is part of the foundation of both structured design and object-oriented design. In structured design, the notion of “black boxes” comes from information hiding. In object-oriented design, it gives rise to the concepts of encapsulation and modularity, and it is associated with the concept of abstraction.

      ​ ——From Code Complete Section 5.3

      ​ 当我们使用到Object Oriented的程序设计,常常会提及信息隐藏(Information Hiding)这一概念。在面向对象的封装、继承、多态中,信息隐藏是一个十分基础的要素。在我看来,信息隐藏主要降低了程序复杂性。隐藏信息的主要机制之一是封装 -组合元素以创建更大的实体。然后,程序员可以专注于新对象,而不必担心隐藏的细节。当我们实现新的功能时,只需要调用原来的接口即可实现基础的功能,并且保证了安全性和健壮性。

      ​ 信息隐藏的原则主要包括:

      ​ a. 多层设计中的层与层之间加入接口层;

      ​ b. 所有类与类之间都通过接口类访问;

      ​ c. 类的所有数据成员都是private,所有访问都是通过访问函数实现的;

      ​ 在我们这次的编程工作中,广泛应用了信息隐藏原则,例如:

      class Container {
      private:
      	int id;
      	string name;
      }
      
      1. Interface Design & Loose Coupling

        面向接口编程(Interface Design)在这次编程中起到了非常重要的作用。GUI和Core两套模块,通过core提供的run() 接口,实现无缝兼用。先把主要的任务逻辑线提取出来,作为接口,core具体实现通过该接口的实现类来完成。

        面向接口编程能够实现loose coupling(松耦合),降低出现bug的概率,使得我们拓展功能更加方便。

        int run(int argc, char** argv) {
        	int n;
        	char P;
        	ofstream outFile;
        	string inFileName, outFileName;
        	inFileName = argv[2];
        	outFileName = argv[4];
        	ifstream inFile(inFileName);
        	outFile.open(outFileName);
        	if (!inFile.is_open() || !outFile.is_open()) {
        		cerr << "core exception : -2, file not opened" << endl;
        		return -2;
        	}
        	inFile >> n;
        	try {
        		while (n--) {
        			inFile >> P;
        			insertShape(inFile, P, container);
        		}
        	} catch (exception & e){
        		cerr << "core exception : -1, overlap" << endl;
        		return -1;
        	}
        	cout << container->lineVec.size() << endl;
        	cout << container->circleVec.size() << endl;
        	unordered_set<Point, myHash>* pointSet = finalGetCrossPoints(container->lineVec,
        		container->circleVec, debug, testPerform);
        	cout << pointSet->size() << endl;
        	outFile << pointSet->size();
        	writePoints(pointSet);
        	return 0;
        }
        

        如上为最主要的接口,实现读取文件、计算交点并输出的核心功能。在GUI部分,只需要调用.dll文件,即可完成核心功能的使用。

    4. 计算模块接口的设计与实现过程。设计包括代码如何组织,比如会有几个类,几个函数,他们之间关系如何,关键函数是否需要画出流程图?说明你的算法的关键(不必列出源代码),以及独到之处。(7')

      1. 类(UML)图:

        8bOlgP.jpg

        类包括:Shape, Point, Line, Circle, Container.

        ​ 其中,Line, CircleShape的子类,采用面向对象的方法来维护可拓展性、可维护性良好的程序结构。

        • Shape: 二维几何形的类。具有基本的共有属性和方法。
        • Point:点类。

        ​ 具有属性:坐标x, y,脏位 valid

        ​ 具有方法:

        • Line:直线类。

        ​ 具有属性:斜率k,截距b,垂直布尔值vertical,垂直截距vertical_x,线类型 lineType(表明是直线、线段还是射线)。

        ​ 具有方法:

        show():显示对象属性。

        getCross(Line* l2):获得与另一条直线的交点。

        setX(double x):获得直线上横坐标为x的点。

        • Circle:圆类。

        ​ 具有属性:圆心P,半径r

        ​ 具有方法:

        bool isCross(Line l):判断圆是否与直线相交。

        bool isCross(Circle c2):判断两圆是否相交。

        pointPair intersections(Circle* c):获取两圆的交点。

        pointPair getCrossPoints(Circle* cir, Line* l):获取圆和直线的交点。

      数据的组织

      使用STL中的vector来管理Line。为了不使点重复,使用set管理获得的点。但是set的性能较慢,需要进行排序,而这一点在本次作业中没有要求。改为使用unordered_set来管理Point

      同时使用 Container类储存图形对象:

      class Container {
      private:
      	int id;
      	string name;
      
      public:
      	unordered_multimap<double, Shape*> map;
      	vector<Line*> lineVec;
      	vector<Circle*> circleVec;
      	bool equal(const Shape &s1, const Shape &s2);
      	Shape* find(const Shape &shape);
      	bool insert(Shape &shape);
      
      	double myHashFunc(const Shape &obj);
      };
      

      算法

      题目的实际需求就是求解多条直线/线段/射线的交点。除了正确性的要求之外,还要尽可能提高性能。那么,首先考虑如何求解:

      • 若已知两条直线方程 (L_1: y = k_1x + b_1, L_2: y = k_2x + b_2),且(k_1 eq k_2),可直接联立得到解。

      • 若有一条直线是垂直的,(L_1: x = x_0, L_2: y = k_2x + b_2),可带入求解。

      • 若两条直线平行,没有交点。

        直线方程的公式有以下几种形式

        斜截式:

        [egin{equation}y=kx+bend{equation} ]

      ​ 截距式:

      ​ $$egin{equation}x/a+y/b=1end{equation}$$

      ​ 两点式:

      ​ $$egin{equation}(x-x1)/(x2-x1)=(y-y1)/(y2-y1)end{equation}$$

      ​ 一般式:

      ​ $$egin{equation}ax+by+c=0end{equation}$$ (可以表达任意直线)

      ​ 在这里采用斜截式,只需要建立直线的时候计算(k, b)(或垂直(x)轴),空间复杂度小,在之后的计算中也非常简洁。

      ​ 以上为基本求解方法。

      ​ 那么,如何对所有直线求解呢?

      ​ 最直观的想法是对所有直线分别求交点,时间复杂度为(O(n^2))。但是,这样粗暴的解法的性能无疑是很低的。在网络上查找资料,想到平行线可以作为一个集合,集合内不用求交点。但是,要求平行线集合仍然要遍历它们的斜率,和直接求交点的时间复杂度并无太大区别,因为在求交点的时候,若斜率相同,那么可以直接跳过。

      ​ 另外,由于存在多条直线交于同一点的情况,实际上对于所有可能的交点,都要求出具体的交点坐标。那么,平行线集合的优势也被进一步削弱了。所以总体上,这一类做法的复杂度都是(O(n^2))

      ​ 在这一基础上,可以做一些初步的优化,即已经求过交点的两条直线不再求解。如:有直线(L_1, L_2),在求解(L1)(L_2)的交点之后,不需要反过来求交点。如此可以节省一半的时间。

      ​ 其次,在确定算法之后,考虑数据的精度问题。在题目直线参数为(-100000, 100000)的情况下,float精度是达不到要求的。只有使用double才能满足精度约束。

      ​ 在求解直线交点的基础上,可以对射线、线段用类似的方法求解。假设为直线求出交点,然后判断其有效性(根据是否是真实的交点)。

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

      见上题。

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

      本项目做出的优化:

      1. 采用unordered_set保存数据,避免排序带来的性能消耗。
      2. 跳过已经计算过的几何形,避免重复计算。

      构思优化消耗时间10min,实现优化消耗25min左右。

      关于为什么没有做平行线的优化:

      ​ 考虑平行线之间不用互相计算交点,节省了时间。但是得到平行线集合需要消耗不小的时间,而且多条线交于同一点要求所有几何形之间都要计算出准确的交点位置。平行线优化并没有展现出优势,但是会加大程序的复杂度。

      性能分析:

      ​ 所有分析采用随机生成的 (N = 2500) 组数据。

      CPU性能分析:

      8HrFv8.png

      占用最大CPU的为hash函数,这一项难以优化。

      内存占用分析:

      8HrP8P.png

    7. 看 Design by Contract,Code Contract 的内容:

      描述这些做法的优缺点,说明你是如何把它们融入结对作业中的。(5')(独立完成)

      这两个页面讲述的内容主要为契约式设计。类似于企业协作:

      • 供应商必须提供一定的结果(义务),并有权期望客户满足必要的条件(利益)。
      • 客户必须满足这些条件(义务),并有权获得结果(收益)。
      • 双方必须遵守适用于所有合同的某些一般法律和法规。

      Design by Contract将这些元素变成软件文本的组成部分:前提条件(客户义务,供应商收益);后置条件(客户利益,供应商义务);和不变量(一般规则)。这些概念存在于各个级别:需求,设计,实施,测试。

      优点:

      • 自动的高质量编程。
      • 内置的框架,可进行集中且有效的测试。
      • 无需描述实现细节即可传达清晰的设计的能力。
      • 整个软件开发过程的清晰,简单。
      • 项目管理更加直接、稳定。
      • 有利于构建无错误的面向对象系统。
      • 有助于程序员更好地理解和控制继承机制。
      • 可为异常处理提供安全有效的语言构造。

      缺点:

      • 更改的开发成本
      • 更多的开发时间
      • 潜在的开发错误(由于程序员不熟悉Design by Contract)

      在我们的程序中:

      bool checkOneLine(string s) {
      	vector<string> ss;
      	split(s, ss);
      	if (ss.size() == 0) {
      		cerr << "console exception:-4, checkOneLine" << endl;
      		return false;
      	}
      	string types = ss.at(0);
      	if (typeSet.find(types) == typeSet.end()) return false;
      	try {
      		if (types == "L" || types == "R" || types == "S") {
      			myassert(5, (int)ss.size());
      		} else {
      			myassert(4, (int)ss.size());
      		}
      		for (int i = 1; i < ss.size(); i ++) {
      			myassert(checkNumber(ss.at(i)), true);
      		}
      	} catch (exception e) {
      		cerr << "console exception:-4, checkOneLine" << endl;
      		return false;
      	}
      	return true;
      }
      

      广泛使用动态检查,对输入内容和输出完成契约式的设计。从而确保了在程序运行过程中的安全性和稳定性。

    8. 计算模块部分单元测试展示。展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路。并将单元测试得到的测试覆盖率截图,发表在博客中。要求总体覆盖率到 90% 以上,否则单元测试部分视作无效。(6')

      8L31cd.md.png

      如图所示,单元测试的时候要考虑各种组合情况,比如这个函数是测试图形之间是否相互重叠导致有无数个交点的,这个函数是考察直线直线、直线射线、射线射线之间重叠和不重叠的情况,还要考虑线段与线段,线段与直线,线段与射线,之间重叠,部分重叠和线段完全被包含在其中等等条件。除此之外,还要考虑边界条件,比如数据上下界,-99999,99999这种条件。

      ​ 在测试完计算功能之后是测试错误处理部分,有些部分仅涉及函数,有些涉及文件,因此不但要写单元测试代码,还得准备测试用的样例文件。

      ​ 我使用的是vs2019社区版,社区版中没有统计单元测试覆盖率的功能。OpenCppCoverage Plugin也不行,这个插件只能检测运行一次调试时的代码覆盖率,无法检测单元测试的覆盖率,而由于有错误处理,我们没办法在一次调试中覆盖90%的代码,这个插件与单元测试无关。实际情况下,我们的单元测试达到了基本完全的覆盖率。

    9. 计算模块部分异常处理说明。在博客中详细介绍每种异常的设计目标。每种异常都要选择一个单元测试样例发布在博客中,并指明错误对应的场景。(5')

      • 输入的指令错误

        8L3JBt.png

      • 输入的文件无法打开

        8L3YHP.png

      • 目录下没有"in.txt"的输入文件存在输入的文件中有非法字符(以下采用单元测试中调用不同的输入文件来进行测试)

        8L8SKA.png
        8L3vgH.png

      • 输入的文件中数字越界(以下采用单元测试中调用不同的输入文件来进行测试)

        8L80IK.png

      • 输入的文件中的直线、射线、或线段的定义中存在两个重合的点(以下采用单元测试中调用不同的输入文件来进行测试)

        8L8fdP.png
        8L8gsA.png
        8L82qI.png

      • 输入文件的行数不匹配(以下采用单元测试中调用不同的输入文件来进行测试)

        8L8oRg.png
        8L8HMj.png

      • 输入的文件中有两个图形重合导致有无数个交点(以下采用单元测试中调用不同的输入文件来进行测试)

        8L8bss.png

    10. 界面模块的详细设计过程。在博客中详细介绍界面模块是如何设计的,并写一些必要的代码说明解释实现过程。(5')

      GUI部分主要借助QT来实现的,QT可以通过拖拽组件的方式来进行UI设计,并可以使用Signals/Slots的方式实现模块之间的联动。

      8L8xiT.md.png

      主要设计如图所示,所有组件统一展示在窗口中。按钮和输入的窗口上方有相应的文字说明。正上方占主要部分的是进行展示的窗口,右下角的交点坐标框用来展示计算出来的交点。

    11. 界面模块与计算模块的对接。详细地描述 UI 模块的设计与两个模块的对接,并在博客中截图实现的功能。(4')

      8LGUSg.png

      两个模块主要通过文件进行数据传输,由于计算的核心组件已经封装成DLL文件,所以UI模块将添加或删除的直线写入临时文件"temp.txt"中,在点击绘制按钮后,通过调用函数的方式调用核心组件计算交点,并写入另一个临时文件"points.txt"中,完成之后从读取文件"temp.txt“中图形,展示到上方的窗口中,在从临时文件"points.txt"中读取交点坐标展示到右下角的窗口中。

    12. 描述结对的过程,提供两人在讨论的结对图像资料(比如 Live Share 的截图)。关于如何远程进行结对参见作业最后的注意事项。(1')

      结对使用腾讯会议软件进行实时投屏和通话,保证交流的高效率。一人写代码,一人审查。

      结对过程中,我们大体上是积极的、充满干劲、相互督促的。在一人编码过程中,另一人负责检查的监督,确保代码的质量和bug的最小化。同时,我们技术观点相近,结对非常愉快。结对使用了:一个人写,一个人看;或者一个人写实现,一个人写测试两种方式。一段时间后进行交换。

      8LpxPS.png

    13. 看教科书和其它参考书,网站中关于结对编程的章节,例如:http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html ,说明结对编程的优点和缺点。同时描述结对的每一个人的优点和缺点在哪里(要列出至少三个优点和一个缺点)。(5')(独立完成)

      结对编程的优点:

      1. 有利于知识的分享和传递。当一方不熟悉某一知识领域时,另一方可以进行协助。
      2. 清晰分工。在整个过程中,二人都有核心的事情要做,但是会在一些边缘的事情上,看情况调节工作。这样清晰的分工可以让每个人专注在自己的事情上,快速高效的输出。
      3. 互相学习。除了编程上的姿势,更重要的是学习对方做事的态度和方式,
      

      结对编程的缺点:

      1. 结对编程非常消耗精力。要求结对的双方都要保证有充沛的精力。
      2. 结对的双方如果脾气对味,技术观点相近,结对会很愉快,而且碰撞出很多火花,效率有明显提高。反之,就可能陷入很多的争吵,而导致进度停滞不前。甚至影响团队协作。
      3. 不是所有的工作都适合结对。技术验证和架构设计都不适合结对。结对比较适合在需求和架构设计明确后的实现阶段。
      4. 结对编程不能替代代码评审。虽然结对编程对代码的审核程度比代码评审细致的多。但两个结对的人有明显的思维趋同性,从而忽略同样的问题或者犯下同样的错误。
      

      本人的优点:

      1. 对程序的框架有清晰的规划
      2. 较强的问题分析处理能力
      3. 坚强的意志,不惧怕程序编写中的困难
      

      本人的缺点:

      1. 在编程的细致程度上有所欠缺
      

      队友的优点:

      1. 旺盛的编程精力
      2. 乐于学习新知识
      3. 强大的编码能力
      

      队友的缺点:

      1. 偶尔出现粗心的编程错误
      
    14. 在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。(0.5')(独立完成)

      见最上方表格。

    15. 运行实例

      命令行程序:

      .inintersect.exe -i in.txt -o out.txt
      

      GUI程序:

      位于binGUIintersect.exe

    :结对小组中两个人发布独立博客,其中 2、3、5、7、13、14 部分请独立完成,不允许雷同。项目的测试分数两人共享,博客的分数各自独立。附加题的相关要求请按附加题的要求补充在博客中。

  • 相关阅读:
    php 数据库练习之租房子
    php数据访问之查询关键字
    Objective-C代码学习大纲(3)
    Objective-C代码学习大纲(2)
    Objective-C代码学习大纲(1)
    简介Objective-C语言
    为什么Objective-C很难
    Swift之 ? 和 !
    使用Mac App Store更新、下载软件时出现未知错误的解决方法
    如何激励用户为你的app评分?
  • 原文地址:https://www.cnblogs.com/kidogucb/p/12558125.html
Copyright © 2011-2022 走看看