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

    项目 内容
    这个作业属于哪个课程/ 2020年春季计算机学院软件工程(罗杰 任健)
    这个作业的要求在哪里? 结对项目作业
    我在这个课程的目标是? 提高代码水平,熟悉团队合作
    这个作业在哪个具体方面帮助我实现目标? 分析成熟软件的优缺点,从中学习软件开发、设计经验

    1. 教学班级和可克隆的 Github 项目地址。

    2. PSP 表格

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

    3. 运用到的设计方法。

    • 信息隐藏:UI模块在调用计算模块的接口时,只将参数传递给了就算模块,计算模块的细节全部被隐藏。添加删除画图等等功能全部被封装在Core中,UI只负责相应用户界面。但类的属性是共有的,没有遵循信息隐藏原则。
    • 松耦合:我们将判断直线是否重合、点是否在线上、画图等功能作为类的方法实现,这样只需修改相应的方法,而不用修改调用、UI模块等。

    4. 计算模块接口的设计与实现过程。

    • 本次作业增加了射线、线段类。根据需求的改变,我们初步预计应添加两个类。为了尽量少地重构代码,我们选择将直线类作为射线、线段类的父类。

      • 线段类添加了startXstartYendXendY 四个属性,分别用来标记线段的起点和终点坐标。继承了构造函数,用来初始化这些属性。
      • 射线类添加了startXstartYdirect 三个属性,用来记录射线的起点和射线的方向。
      • 射线、线段、类继承了直线类的方法isInLineisRepeatdrawshow,分别用来判断点是否在直线上、判断几何图形之间是否有无数个焦点(重合)、在画布上画出几何图形、展示几何图形信息。

      圆、点类与个人项目相同,没有做更改。

    • 四个接口来实现几何图形的增删功能:addLineaddCircledelLinedelCircle

      • addLineaddCircle两接口需要传入对应几何图形的参数。``addLine首先需要传入LS或者R来区分直线、线段和射线,其次传入两个点的坐标。addCricle`只需要传入三个参数,分表代表圆心的横纵坐标以及圆的直径。
      • delLinedelCircle只需要传入一个参数,即相应几何对象在相应容器(vector)内的下标。
    • 两个接口来展示几何图形的详细信息:showLineshowCircle

      • showLine会调用LineSegmentLineRaysLine的show方法,展示相应几何图形的详细信息:直线参数,起点终点坐标,射线方向等。并展示几何图形在容器vector<Line*>lines中的下标。
      • showCircle会直接展示Circle的三个参数以及在容器vector<Circle>circles中的下标。
    • solve接口来计算几何图形之间的交点,基本保持个人项目代码不变。

    • 两个接口进行几何图形的绘制:drawdrawPoint。调用了graphics.h头文件来绘制几何图形。

    • inputFile接口用来读取指定路径下的文件。

    • 算法关键

      • 对本次作业而言,我们采用的求交点方法仍然与上次作业相同。首先求出线段或者射线所在的直线与其他几何对象的交点,关键之出在于判断交点是否在线段、射线上。
      • 我们使用了继承,避免了大规模的代码重构,并通过类的方法isInLine 来判断。

    5. UML

    6. 计算模块接口部分的性能改进

    我们用时最长的函数是lineIntersectLine 函数,经过性能分析可以看出是vector的insert方法耗时最长。考虑到更换容器类需要一定规模的代码重构,1000条直线运行时间大概为5.9秒的性能基本满足了我们的需求,我们并未对此进行优化。

    7. Design by Contract 与 Code Contract

    契约式编程

    它规定软件设计者应该为软件组件定义正式的,精确的和可验证的接口规范,该规范扩展了抽象数据类型的普通定义,包括前置条件,后置条件和不变量。

    • 优点:使用者和被调用者地位平等,双方必须彼此履行义务,才可以行驶权利。调用者必须提供正确的参数,被调用者必须保证正确的结果和调用者要求的不变性。双方都有必须履行的义务,也有使用的权利,这样就保证了双方代码的质量,提高了软件工程的效率和质量。
    • 缺点:契约式编程并未被标准化,因此项目之间的定义和修改各不一样,给代码造成很大混乱。
    • 在结对作业中,我们没有使用到契约式编程。但在计算模块和UI模块的对接上,我们通过口头约束,规定了一些接口的参数、功能等等。

    8. 计算模块部分单元测试展示。

    • 射线所在的直线与圆相交,但交点不在射线上。射线分别为四个方向:若不垂直于X轴,沿X轴正方向,沿X轴负方向;若垂直于X轴,分为沿Y轴正方向,沿Y轴负方向。针对四种情况分别构建测试用例。

      Line l1(0, 1, 1, 0);
      RaysLine r1(1, 1, 2, 2);
      lineIntersectLine(l1, r1);
      Assert::AreEqual((int)points.size(), 0);
      RaysLine r2(1, 1, 1, 2);
      lineIntersectLine(l1, r2);
      Assert::AreEqual((int)points.size(), 0);
      RaysLine r3(0, 0, -1, -1);
      lineIntersectLine(l1, r3);
      Assert::AreEqual((int)points.size(), 0);
      RaysLine r4(-1, -1, -1, -2);
      lineIntersectLine(l1, r4);
      Assert::AreEqual((int)points.size(), 0);
      points.clear();
      
    • 线段所在的直线与圆相交,但交点不在射线上。大体分为两种情况:线段与X轴垂直,线段与X轴不垂直。其中为了测试构造函数初始化startX、startY和endX、endY,又设计了同一组参数交换前后两个点的情况。

      Line l1(0, 1, 1, 0);
      SegmentLine s1(1, 1, 2, 2);
      lineIntersectLine(l1, s1);
      Assert::AreEqual((int)points.size(), 0);
      SegmentLine s2(1, 1, 1, 2);
      lineIntersectLine(l1, s2);
      Assert::AreEqual((int)points.size(), 0);
      SegmentLine s3(1, 2, 1, 1);
      lineIntersectLine(l1, s3);
      Assert::AreEqual((int)points.size(), 0);
      SegmentLine s4(-1, -1, -2, -2);
      lineIntersectLine(l1, s4);
      Assert::AreEqual((int)points.size(), 0);
      SegmentLine s5(-1, -1, -1, -2);
      lineIntersectLine(l1, s5);
      Assert::AreEqual((int)points.size(), 0);
      points.clear();
      
    • 射线与圆相交

      Circle c1(0, 0, 1);
      RaysLine r1(0, 1, 1, 1);
      lineIntersectCircle(r1, c1);
      Assert::AreEqual((int)points.size(), 1);
      Assert::AreEqual(points.begin()->x, 0.0);
      Assert::AreEqual(points.begin()->y, 1.0);
      points.clear();
      
    • 线段与圆相交

      Circle c1(0, 0, 1);
      SegmentLine s1(0, 1, 0, -1);
      lineIntersectCircle(s1, c1);
      Assert::AreEqual((int)points.size(), 2);
      set<Point>::iterator it = points.begin();
      Assert::AreEqual(it->x, 0.0);
      Assert::AreEqual(it->y, -1.0);
      it++;
      Assert::AreEqual(it->x, 0.0);
      Assert::AreEqual(it->y, 1.0);
      points.clear();
      
    • inputArg函数测试

      char* argv[5] = { "Intersect.exe", "-i", "in.txt", "-o", "out.txt" };
      inputArg(5, argv);
      Assert::AreEqual((int)lines.size(), 3);
      Assert::AreEqual((int)circles.size(), 1);
      lines.clear();
      circles.clear();
      fin.close();
      fout.close();
      
    • solve等函数的测试沿用上次作业的代码

    • 代码覆盖率测试

    9. 计算模块部分异常处理说明。

    • 命令行参数

      • 没有正确使用-i或者-o参数,而是输入了其他-?参数

        try {
        	char* argv[5] = { "Intersect.exe", "-v", "in.txt", "-o", "out.txt" };
        	inputArg(5, argv);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "Incorrect command line parameters, please use '-i' for input, '-o' for output");
        }
        
      • 缺少-i参数

        try {
        	char* argv[5] = { "Intersect.exe", "iii", "in.txt", "-o", "out.txt" };
        	inputArg(5, argv);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "'-i' is not found, please use '-i'");
        }
        
      • 缺少-o参数

        try {
        	char* argv[5] = { "Intersect.exe", "-i", "in.txt", "oooo", "out.txt" };
        	inputArg(5, argv);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "'-o' is not found, please use '-o'");
        }
        
      • 未找到输入文件

        try {
        	char* path = "int.txt";
        	inputFile(path);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "can not locate input file, please check the path of the file");
        	fin.close();
        }
        
    • 文件输入参数错误

      • 第一行不是整数

        try {
        	char* path = "case1.txt";
        				inputFile(path);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "Incorrect Num at line 1, the Num must be a positive integer");
        	fin.close();
        }
        /*文件内容:
        SSSSSS
        C 3 3 3
        S 2 4 3 2
        L -1 4 5 2
        R 2 5 -1 2
        
        */
        
      • 类型错误

        try {
        	char* path = "case2.txt";
        	inputFile(path);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "Incorrect type, correct types are L, S, R, C");			fin.close();
        }
        /*文件内容:
        4
        MAA 3 3 3
        A 2 4 3 2
        L -1 4 5 2
        R 2 5 -1 2
        
        */			
        
      • 圆参数错误(缺参数或参数不为整数)

        try {
        	char* path = "case3.txt";
        	inputFile(path);
        }
        	catch (char* msg) {
        	Assert::AreEqual(msg, "Incorrect circle parameter, please input integer and check the number of parameters");
        	fin.close();
        }
        /*文件内容:
        4
        C 3 3 a
        S 2 4 3 2
        L -1 4 5 2
        R 2 5 -1 2
        
        */	
        
      • 直线参数错误(缺参数或参数不为整数)

        try {
        	char* path = "case4.txt";
        	inputFile(path);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "Incorrect line parameter, please input integer and check the number of parameters");
        	fin.close();
        }
        /*文件内容:
        4
        C 3 3 3
        S 2 4 3 2
        L -1 4 5 2
        R 2 5
        
        */
        
      • 几何图形参数超出范围

        try {
        		addLine("R", 1, 2, 3, 100000);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "parameter out of range, correct range is (-100000, 100000)");
        }
        
      • 直线参数两点重合

        try {
        	addLine("R", 1, 1, 1, 1);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two points coincide in a line definition, please input two different points");
        }
        
      • 线与线(包括直线、射线、线段)重合

        try {
        	addLine("L", 1, 1, 2, 2);
        	addLine("L", 3, 3, 4, 4);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        //射线与直线重合
        try {	
            addLine("R", 1, 1, 2, 2);
        	addLine("L", 3, 3, 4, 4);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        //射线与射线重合
        try {
        	addLine("R", 1, 1, 2, 2);
        	addLine("R", 3, 3, 4, 4);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        try {
        	addLine("R", 0, 0, 1, 1);
        	addLine("S", 1, 1, -1, -1);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        try {
        	addLine("S", 0, 1, 0, -1);
        	addLine("L", 0, 0, 0, 2);
        	}
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        //线段与射线重合
        try {
        	addLine("S", 1, 1, -1, -1);
        	addLine("R", 0, 0, 1, 1);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "two lines coincide");
        	lines.clear();
        }
        
      • 圆的半径必须是正整数

        try {
        	addCircle(1, 2, -3);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "radius of circle must be a positive integer");
        }
        
      • 圆重复

        try {
        	addCircle(1, 2, 3);
        	addCircle(1, 2, 3);
        }
        catch (char* msg) {
        	Assert::AreEqual(msg, "this circle exists");
        }
        

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

    • UI模块我们采用了MFC

      初始设计:

      最终设计:

      UI中,第一行可输入文件路径,点击import可导入文件。导入成功会弹出窗口表示成功。不成功会又相应的异常提示。
      第二行五个窗口可分别传入五个参数,第一个敞口可传入L、R、S分别代表直线、射线、线段。后四个窗口可传入两个点的坐标。点击AddLine可将几何图形添加。添加成功会弹出出succeed窗口,不成功会有异常提示。DelLine按钮前的窗口可输入一个数字,代表容器内集合对象的下表,点击DelLine可删除该集合对象。ShowLines可查看容器内各集合对象的详细信息和下标。

      第三行同第二行。

      Draw按钮可画出当前所有的集合对象。Solve可以求出交点,并画出交点。关闭弹出的画图界会终止整个程序。

      各个按钮的代码实现

      • Draw

        void CUIDlg::OnBnClickedButton1()
        {
            //直接调用接口
        	draw();
        }
        
      • Solve

        void CUIDlg::OnBnClickedButton2()
        {
            //直接调用接口
        	solve();
        	draw();
        	drawPoint();
        }
        
      • Import

        void CUIDlg::OnBnClickedButton3()
        {
        	try {
        		CString cstr;
        		GetDlgItemText(IDC_EDIT1, cstr);
        		int n = cstr.GetLength();
        		int len = WideCharToMultiByte(CP_ACP, 0, cstr, n, NULL, 0, NULL, NULL);
        		char* path = new char[len + 1];
        		WideCharToMultiByte(CP_ACP, 0, cstr, n, path, len, NULL, NULL);
        		path[len] = '';
        		inputFile((char*)path);
        
        		string str = "import from file succeed!";
        		string cap = "Message";
        		MessageBox((CString)str.c_str(), (CString)cap.c_str());
        	}
        	catch (const char* msg) {
        		CString cmsg = s2c(msg);
        		AfxMessageBox(cmsg);
        		return;
        	}
        }
        
      • AddLine

        void CUIDlg::OnBnClickedButton4()
        {
        	// 从编辑框读入数据,转化数据类型,调用接口
            // 在这里对读入的数据进行了处理
            // 会对空数据和错误的数据进行预处理,确保传入的参数是正确的
        	try {
        		CString ctype, cx1, cy1, cx2, cy2;
        		GetDlgItemText(IDC_EDIT2, ctype);
        		GetDlgItemText(IDC_EDIT3, cx1);
        		GetDlgItemText(IDC_EDIT4, cy1);
        		GetDlgItemText(IDC_EDIT5, cx2);
        		GetDlgItemText(IDC_EDIT6, cy2);
        		if (ctype.GetLength() == 0 || cx1.GetLength() == 0 || cy1.GetLength() == 0
        			|| cx2.GetLength() == 0 || cy2.GetLength() == 0) {
        			throw "type or parameters can not be NULL";
        		}
        		string type = c2s(ctype);
        		string x1 = c2s(cx1);
        		string y1 = c2s(cy1);
        		string x2 = c2s(cx2);
        		string y2 = c2s(cy2);
        		if (type != "L" && type != "R" && type != "S") {
        			throw "Line type must be L, R or S!";
        		}
        		if (!isDigit(x1) || !isDigit(y1) || !isDigit(x2) || !isDigit(y2)) {
        			throw "parameters must be integer!";
        		}
        		addLine(type, (long long)stoi(x1), (long long)stoi(y1), 
        			(long long)stoi(x2), (long long)stoi(y2));
        
        		string str = "Add line succeed!";
        		string cap = "Message";
        		MessageBox((CString)str.c_str(), (CString)cap.c_str());
        	}
        	catch (const char* msg) {
        		CString cmsg = s2c(msg);
        		AfxMessageBox(cmsg);
        	}
        }
        
      • DelCircle

        void CUIDlg::OnBnClickedButton8()
        {
        	try {
                //同上
        		CString cindex;
        		GetDlgItemText(IDC_EDIT11, cindex);
        		if (cindex.GetLength() == 0) {
        			throw "index can not be NULL";
        		}
        		string index = c2s(cindex);
        		if (!isDigit(index)) {
        			throw "index must be integer!";
        		}
        		delCircle((long long)stoi(index));
        
        		string str = "Delete Circle succeed!";
        		string cap = "Message";
        		MessageBox((CString)str.c_str(), (CString)cap.c_str());
        	}
        	catch (const char* msg) {
        		CString cmsg = s2c(msg);
        		AfxMessageBox(cmsg);
        	}
        	
        }
        
      • ShowCircle

        void CUIDlg::OnBnClickedButton9()
        {
        	// 直接调用接口
        	showCircle();
        }
        

    11. 界面模块与计算模块的对接

    • 两模块的对接如10中给出的代码。对接时直接导入dll文件和lib文件,添加头文件:

      #pragma once
      #pragma comment(lib, "Core.lib")
      #include <string>
      using namespace std;
      extern "C" _declspec(dllimport) void addLine(string type, long long x1, long long y1, long long x2, long long y2);
      extern "C" _declspec(dllimport) void addCircle(long long x, long long y, long long r);
      extern "C" _declspec(dllimport) void delLine(int index);
      extern "C" _declspec(dllimport) void delCircle(int index);
      extern "C" _declspec(dllimport) void inputFile(char* path);
      extern "C" _declspec(dllimport) int solve();
      extern "C" _declspec(dllimport) void draw();
      extern "C" _declspec(dllimport) void drawPoint();
      extern "C" _declspec(dllimport) void showLine();
      extern "C" _declspec(dllimport) void showCircle();
      

      生成dll文件:

      导入dll文件:

    • 实现的功能

      从文件导入:

      添加几何对象:

      show:

      删除几何对象:

      Draw:

      solve:

    12. 结对过程

    我与17373100窦铮 同学进行了结对

    13. 结对优缺点分析

    • 结队编程

      • 优点:两个人可以互相监督,降低低级错误(拼错单词等);也可以轮流休息,增加效率。
      • 缺点:结队过程中理念相互碰撞时比较难以调解,会降低效率。
    • 合作评价

      伙伴
      优点 编程细心,bug少;脾气好,发生争执也很有耐心;时间管理到位 理解能力强;代码风格规范;能与队友高效交流
      缺点 与人交流容易上头 代码bug多,基础不扎实

    14. 实际花费时间

    见问题2 PSP表格

  • 相关阅读:
    Kendo UI
    Docker
    jQuery DataTables && Django serializer
    MySQL ODBC for Linux
    mongoengine
    Python之多线程
    Python中的正则表达式
    通过恢复目录(Catalogue)进行PDB级别的PITR恢复
    执行PDB的PITR恢复失败的说明
    在PDB级别中如何切换或重建UNDO表空间
  • 原文地址:https://www.cnblogs.com/Pupil59/p/12559877.html
Copyright © 2011-2022 走看看