zoukankan      html  css  js  c++  java
  • 结对项目作业

    结对项目作业

    写在前面:

    项目 内容
    这个作业属于哪个课程 2020计算机学院软件工程(罗杰 任健)
    这个作业的要求在哪里 结对项目作业
    教学班级 006
    项目地址 https://github.com/CrapbagMo/PairProgramIntersect

    在开始实现程序之前,在下述 PSP 表格记录下你估计将在程序的各个模块的开发上耗费的时间。

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

    看教科书和其它资料中关于 Information Hiding,Interface Design,Loose Coupling 的章节,说明你们在结对编程中是如何利用这些方法对接口进行设计的。

    1. 在设计的所有类中,都将属性设置为了 private,通过 setXgetX 等方法访问。

    2. 全局静态变量 PlaneContainer* pc 通过 init_PlaneContainerdispose_PlaneContainer 方法访问,对外隐藏了指针。

    3. 在接口向外传递异常时,考虑到 C# 的异常类和 C++ 的异常类并不兼容。我们也没有什么好办法解决这个问题,于是决定采用传统 C 用返回值传递错误状态的思想。使用返回值来传递异常。

    4. 在core.dll中,我们并不依赖任何外部项,完全依靠自身即可完成对外接口提供的功能。


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

    1. 类及函数

      • 五个主要类:

        点类:class Point
        图形类:class Figure
        线条类:class Line: Figure
        圆形类:class Circle: Figure
        平面容器类:class PlaneContainer

      • 五个主要函数:

        添加图形:int add_Figure(std::string buf)
        初始化容器:void initial_PlaneContainer()
        释放容器:void dispose_PlaneContainer()
        获取交点序列:double* get_IntersectionPoints()
        获取交点数目:int get_NumOfIntersectionPoints()

    2. 主要作用:

      • CircleLine类继承自Figure类,实现std::set<Point>intersect(Figure*)方法。该方法返回两个图形的交点集合。

      • 五个函数都是对外提供的接口函数。init_PlaneContainer、dispose_PlaneContainer采用单例模式用于维护全局静态变量PlaneContainer* pcadd_Figureget_NumOfIntersectionPointsget_IntersectionPoints用于提供添加图形、获取交点序列、交点数目功能。

      • 异常由add_Figure函数捕获并处理,处理完后已错误代码形式返回,具体处理方式本节不做介绍。

    3. 算法关键及独到之处:

      • 首先考虑直线和圆的情况(先不考虑射线和线段):

        按照直线和直线, 直线和圆, 圆和圆在平面上的关系分为下面三种情况考虑:

        直线和直线:

        1. 判断直线是否相交: (A_1*B_2-A_2*B_1!=0)则相交.

        2. 若相交则求交点: ((frac{B_1*C_2-B_2*C_1}{A_1*B_2-A_2*B_1},frac{A_2*C_1-A_1*C_2}{A_1*B_2-A_2*B_1}))

        直线和圆:

        1. 联立直线和圆方程(为了起见简便, 若(B!=0), 化为斜截式再联立), 求得系数(tA), (tB), (tC).

        2. 根据(Delta=tB^2-4*tA*tC)判断交点个数

        3. (Deltage0) , 根据求根公式求得交点横坐标, 进而求出交点.

        圆和圆:

        1. 计算圆心距(dis=sqrt{(x_1-x_2)^2+(y_1-y_2)^2}).

        2. 比较圆心距(dis) 和半径和(r_1+r_2), 半径差$ | r_1-r_2 | $ .

        3. 若有交点则两圆相减求出相交弦方程, 进而转化为直线和圆得交点.

      • 注意到直线、射线、线段的不同点仅在于其坐标范围不同。因此为Line类引入范围属性((R leq x leq S))即可同时表示三种线,其中射线、直线的无穷端以宏INF表示。求交点时,将LineFigure求交点,求得的交点再判断是否在Line所在范围内即可,若不在范围内则剔除。这就将直线、射线、线段统一了。


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

    • UML实体图:


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

    • 改进之前,一分钟之内只能运算两百张图不到,运行一分钟(处理了大约150个图形)后性能探测截图:

      可以看到,主要时间都花在了,PlaneContainer.insert方法的set_union函数上。

      经查阅资料发现,在set_union函数中,存在了多次set复制,效率极低。于是修改了此处逻辑如下:

      修改之后,可以在20秒左右完成2000个图形的计算,性能大大提高,与改前可谓“天壤之别”再次执行了性能探查:

      此时PlaneContainer.insert方法已经不耗费太多时间了。


    看 Design by Contract,Code Contract 的内容:

    描述这些做法的优缺点,说明你是如何把它们融入结对作业中的。

    • 关于 Design by Contract,Code Contract

      • 优点

        1. 获得更优秀的设计

          可以启发程序员思考程序的先验条件,有助于了理清思路

          契约双方的权力和义务得到了共享,同时获得了清晰的描述

        2. 提高可靠性

          普遍采用断言机制,可靠性高

          帮助开发者更好地理解代码

          方便测试,随调随用

        3. 是优秀的文档

          好的契约设计,代码本身即是注释,即是文档

          契约设计是精确的规范

        4. 复用性高

      • 缺点

        1. 需要花费时间学习契约思想和技术

        2. 契约代码的开发会更加耗时(但可以节省测试,编写文档时间)

        3. 需要大量时间

        4. 只适用于顺序程序,不适用于并行程序。

    • 在本项目中的应用

      • add_Figure函数调用其他函数的设计中,我们使用了契约方式,约定,add_Figure只负责传递格式正确的格式给其他的函数,其他的函数不再做格式检查。

      • PlaneContainer.insert方法中也采用了契约方式,约定,PlaneContainer.insert方法只负责插入一个合法的Figure,但不保证是否会和已有的Figure冲突,这需要由他所调用的方法检查。


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

    • 代码覆盖率情况:(使用VS2017 Enterprise代码覆盖率工具生成)

      对项目中的类的单元测试覆盖率为 (93.07\%) ,对类 Circle 的覆盖率为 (100\%), 对类 Line 的覆盖率为 (98.5\%)

    • 我们设置了针对功能的测试和针对异常的测试。

      1. 针对功能性测试,我们测试了圆、直线、线段和射线的一些函数在正常情况下的功能性,同时也构造了一些特殊情况下的测试用例,比如:

        // 射线 圆 内部相交  
        PlaneContainer pc;  
        pc.insert(new Circle(0, 0, 2));  
        pc.insert(new Line(1, 0, 2, 2, RL));  
        int count = pc.countIntersectionPoints();  
        Assert::AreEqual(count, 1);  
        
        // 射线 射线 一个交点  
        PlaneContainer pc;  
        pc.insert(new Line(0, 0, 1, 1, RL));  
        pc.insert(new Line(0, 0, -1, -1, RL));  
        int count = pc.countIntersectionPoints();  
        Assert::AreEqual(count, 1);  
        
        // 精度测试  
        PlaneContainer pc;  
        pc.insert(new Line(0, -100000, 1, 100000, SL));  
        pc.insert(new Line(0, 0, 0, 1, SL));  
        pc.insert(new Line(0, -99999, 1, -99999, SL));  
        int count = pc.countIntersectionPoints();  
        Assert::AreEqual(count, 3);  
        
      2. 针对异常的测试用例见下一部分。


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

    • 本次结对项目中,我们共设计了七类异常

      1. 输入不满足标准格式

        我们使用正则表达式来识别输入是否满足标准格式,正则表达式如下:

        std::regex segREGEX("S\s+-?\d+\s+-?\d+\s+-?\d+\s+-?\d+\s*\n?");
        std::regex lineREGEX("L\s+-?\d+\s+-?\d+\s+-?\d+\s+-?\d+\s*\n?");
        std::regex rayREGEX("R\s+-?\d+\s+-?\d+\s+-?\d+\s+-?\d+\s*\n?");
        std::regex circleREGEX("C\s+-?\d+\s+-?\d+\s+-?\d+\s*\n?");
        
      2. 参数不在标准范围 (-100000, 100000) 内

      3. 定义直线的两点重合

      4. 圆的半径不大于零

      5. 计算中出现无穷交点

      6. 直线、线段或射线与直线、线段或射线出现部分或完全重合

      7. 两圆完全重合

    • 对于单元测试,我们对每一类异常场景及正常场景设计了3-4个测试样例,测试代码如下,

      TEST_METHOD(TestMethod1)
      {    // 格式错误
          int res = add_Figure("acsd");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod2)
      {    // 格式错误
          int res = add_Figure("C 5 3 -2 1");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod3)
      {    // 格式错误
          int res = add_Figure("L -5 3 -2 0 4");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod4)
      {    // 格式错误
          int res = add_Figure("R 5 -3 2");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod5)
      {    // 格式错误
          int res = add_Figure("c 5 -3 2");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod6)
      {    // 格式错误
          int res = add_Figure("S 5 -3 2 0-1
      ");
          Assert::AreEqual(res, -1);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod7)
      {    // 正常
          int res = add_Figure("R 5 -3 2 3");
          Assert::AreEqual(res, 0);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod8)
      {    // 正常
          int res = add_Figure("C 5 -3 3
      ");
          Assert::AreEqual(res, 0);
          delete(pc);
      }
      
      TEST_METHOD(TestMethod10)
      {    // 半径小于0
          int res = add_Figure("C 5 -3 -2 
      ");
          Assert::AreEqual(res, 3);
      }
      
      TEST_METHOD(TestMethod11)
      {    // 半径等于0
          int res = add_Figure("C -5 -3 0
      ");
          Assert::AreEqual(res, 3);
      }
      
      TEST_METHOD(TestMethod12)
      {    // 点超出坐标轴范围
          int res = add_Figure("L 963214 -3 2 3
      ");
          Assert::AreEqual(res, 1);
      }
      
      TEST_METHOD(TestMethod13)
      {    // 点超出坐标轴范围
          int res = add_Figure("R 5 -3 526151 3
      ");
          Assert::AreEqual(res, 1);
      }
      
      TEST_METHOD(TestMethod14)
      {    // 点超出坐标轴范围
          int res = add_Figure("C -3 526151 3
      ");
          Assert::AreEqual(res, 1);
      }
      
      TEST_METHOD(TestMethod15)
      {    // 两点重合
          int res = add_Figure("R 2 -3 2 -3 ");
          Assert::AreEqual(res, 2);
      }
      
      TEST_METHOD(TestMethod16)
      {    // 两点重合
          int res = add_Figure("L 99999 88888 99999 88888 
      ");
          Assert::AreEqual(res, 2);
      }
      
      TEST_METHOD(TestMethod17)
      {    // 两点重合
          int res = add_Figure("S 0 2 0 2 
      ");
          Assert::AreEqual(res, 2);
      }
      
      TEST_METHOD(TestMethod18)
      {    // 无穷交点
          add_Figure("L 2 2 3 3 ");
          int res = add_Figure("L 0 0 -1 -1 
      ");
          Assert::AreEqual(res, 4);
      }
      
      TEST_METHOD(TestMethod19)
      {    // 无穷交点
          add_Figure("S 2 2 4 4 ");
          int res = add_Figure("S 3 3 0 0 
      ");
          Assert::AreEqual(res, 4);
      }
      
      TEST_METHOD(TestMethod20)
      {    // 无穷交点
          add_Figure("R 1 1 4 4 ");
          int res = add_Figure("S 2 2 0 0 
      ");
          Assert::AreEqual(res, 4);
      }
      
      TEST_METHOD(TestMethod21)
      {    // 无穷交点
          add_Figure("R 1 1 1 0 ");
          int res = add_Figure("S 1 0 1 5 
      ");
          Assert::AreEqual(res, 4);
      }
      
      TEST_METHOD(TestMethod22)
      {    // 无穷交点
          add_Figure("C 1 1 1  ");
          int res = add_Figure("C 1 1 1 
      ");
          Assert::AreEqual(res, 4);
      }
      

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

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

    1. 这两个问题内容有相关联的地方故,合起来说。

    2. 界面模块是基于.NET 4.7.2 的窗体应用,采用C#语言编写。

    3. Program用于定义Main函数外,整个模块一共只有类,下面结合图形、代码介绍该类的组成:

      • 程序界面:

      • 声明部分代码:

        namespace GUI  
        {  
            unsafe public partial class MainForm : Form  
            {  
                //下面为控件类实例,由VS自动生成  
                //Panel是主要的控件,用于绘图  
                private System.Windows.Forms.Panel panel1;  
                //打开输入文件对话框  
                private System.Windows.Forms.OpenFileDialog openFileDialog1;  
                //文件输入按钮和手工输入按钮  
                private System.Windows.Forms.Button inputButton;  
                private System.Windows.Forms.Label pathInput;//保存并显示输入文件路径  
                private System.Windows.Forms.Button button2;  
                //1-6用于获取手工输入图形的数据,7用于显示多行提示信息  
                private System.Windows.Forms.TextBox textBox1;  
                private System.Windows.Forms.TextBox textBox2;  
                private System.Windows.Forms.TextBox textBox3;  
                private System.Windows.Forms.TextBox textBox4;  
                private System.Windows.Forms.TextBox textBox5;  
                private System.Windows.Forms.TextBox textBox6;  
                private System.Windows.Forms.TextBox textBox7;  
                //以下label用于显示单行信息  
                private System.Windows.Forms.Label num;  
                private System.Windows.Forms.Label label2;  
                private System.Windows.Forms.Label label3;  
                private System.Windows.Forms.Label label4;  
                private System.Windows.Forms.Label label5;  
                private System.Windows.Forms.Label label6;  
                private System.Windows.Forms.Label label7;  
                private System.Windows.Forms.Label label8;  
                private System.Windows.Forms.Label label9;  
        
                //下面为用户变量  
                //绘图画笔  
                private Pen pen;  
                //绘图类  
                private Graphics g;  
                //直线容器,每四个数字代表一条直线  
                private LinkedList<int> StraightLines;  
                //射线容器,每四个数字代表一条射线  
                private LinkedList<int> RayLines;  
                //线段容器,每四个数字代表一条线段  
                private LinkedList<int> LineSegments;  
                //圆形容器,每三个数字代表一个圆形  
                private LinkedList<int> Circle;  
        
                //下面的五个函数皆为从core.dll中导入的接口函数  
                //添加图形并返回添加结果  
                [DllImport("core.dll")]  
                private static extern int add_Figure(StringBuilder buf);  
                //初始化容器  
                [DllImport("core.dll")]  
                private static extern void initial_PlaneContainer();  
                //摧毁容器  
                [DllImport("core.dll")]  
                private static extern void dispose_PlaneContainer();  
                //获取交点序列  
                [DllImport("core.dll")]  
                private static extern double* get_IntersectionPoints();  
                //获取交点数目  
                [DllImport("core.dll")]  
                private static extern int get_NumOfIntersectionPoints();  
        
                //下面为成员方法(包括回调函数)  
                //构造方法,主要为变量分配。  
                public MainForm();  
                //窗体打开和关闭的回调,用于显示欢迎和再见提示  
                private void MainForm_Load(object sender, EventArgs e);  
                private void MainForm_FormClosing(object sender, FormClosingEventArgs e)  
                //输入按钮的回调函数,用于处理文件输入  
                private void inputButton_Click(object sender, EventArgs e);//文件输入  
                private void button2_Click(object sender, EventArgs e)//手工输入  
                //画板的重绘回调函数,用于绘制图形  
                private void panel1_Paint(object sender, PaintEventArgs e);  
                //处理add_Figure函数返回来的错误代码  
                private bool processRetVal(int retCode, string line);  
                //存储图形,以供绘图函数使用  
                private void store(string buf);  
                //四个小的绘图函数  
                private void drawLine();  
                private void drawCircle();  
                private void drawRay();  
                private void drawLineSegment();  
                //坐标系变换函数  
                private void change(ref Point p);  
                private void change(ref PointF p)  
            }  
        }  
        
    4. 重点介绍以下几个函数的运行逻辑

      1. private void inputButton_Click(object sender, EventArgs e);//文件输入

        • 若打开不成功则不进行处理并给出提示。

        • 否则,按行循环读取文件送string line,调用add_Figure处理,并获取处理结果送int retCode

        • lineretCode交给processRetVal处理。

        • 通过get_NumOfIntersectionPoints更新交点数目并显示

      2. private void button2_Click(object sender, EventArgs e);//手工输入

        • 通过6个TextBox输入框获取输入数据。
        • 根据LRSC输入框输入的类型代码,将LRSC和后面的相应输入框中内容组成string line,调用add_Figure处理,并获取处理结果送int retCode
        • lineretCode交给processRetVal处理。
        • 通过get_NumOfIntersectionPoints更新交点数目并显示
      3. private bool processRetVal(int retCode, string line);

        • retCode==0,未发生异常,调用storeline存储。

        • 否则,不进行存储并提示相应的错误,提示用户,例如。

      4. private void panel1_Paint(object sender, PaintEventArgs e);

        • 绘制坐标系、刻度等。
        • 调用drawLinedrawCircledrawRaydrawSegment绘制相应图形。
    5. 模块对接

      1. core模块不提供内存管理,由接口函数init_PlaneContainerdispose_PlaneContainer管理。

      2. 核心接口add_Figure供界面模块调用。

      3. 正则匹配,如果格式不符合要求,不进入下一步并返回相应的错误代码。否则予以解析进入下一步。

      4. 根据解析结果,构造相应的Figure并捕获可能的异常,如果构造过程中出现两点重合、半径非正等异常,不进入下一步,并反回相应的错误代码。

      5. 将构造的Figure假如容器pc,并捕获可能的异常,如果出现无穷交点等异常,不进入下一步,并返回相应错误代码。

      6. 正常返回。代码代码0;

    6. 使用图例


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

    • 在项目一开始,我们采用了“领航与-驾驶员”模式,但是在实验一下午之后,由于网络等原因,我们发现“领航员-驾驶员”模式工作效率不太高,于是我们采用了独立写然后互审,互测的方式,工作效率有所提高,并一直沿用到结束。

    • 我们在结对过程中使用了 Visual Studio 中的 Live Share 和微信进行交流,截图如下:

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

    • 关于结对编程

      • 优点

        1. 互相鼓励,不容易沮丧。

        2. 互相监督,不容易偷懒。

        3. 互相学习,双方都能在编程中学到东西。

        4. 两个人随时复审,降低 bug产出率。

      • 缺点

        1. 与合不来的人容易发生冲突,导致团队矛盾。

        2. 双方技术实力差距过大,可能会使得另一方产生不满情绪。

        3. 观点出现分歧容易发生争吵。

        4. 开发时可能会闲谈、走神,降低效率。

    • 关于结对双方

      优点 缺点
      有耐心
      代码风格良好
      善于交流
      粗心
      结对伙伴 细心
      时间观念好
      逻辑清晰
      不善交流

    在你实现完程序之后,在附录提供的PSP表格记录下你在程序的各个模块上实际花费的时间。

    见上文PSP表格。


  • 相关阅读:
    David Cutler NT之父
    VS2012 RC 编译Qt 4.8.2完整过程
    vm demo加固分析
    IDA dump so
    博客园首次发帖
    WebRTC本地选择codec(web本地模拟)
    Android 摄像头预览悬浮窗,可拖动,可显示在其他app上方
    [译] 清除浮动的新方法
    《学习HTML5游戏编程》译记
    Web中的Tip组件实现
  • 原文地址:https://www.cnblogs.com/black-watch/p/12560456.html
Copyright © 2011-2022 走看看