博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
结对编程收获
阅读量:5306 次
发布时间:2019-06-14

本文共 5048 字,大约阅读时间需要 16 分钟。

 

 
 

结对编程收获

沈三景 软件工程 结对编程


 

前言

本次结对编程,过程异常坎坷,遇到了很多以前没有遇到过的问题,比如:如何写API文档才能让UI组更加容易读懂,如何定义接口,如何封装代码等等。但是结果还算不错,学会了一些在正常上课过程中没法学到的东西。


 

结对编程

在结对编程中,比起个人作业多出了两个人交流以及分工的部分,两人分别承担驾驶员和领航员的角色的模式,“驾驶员”负责具体的编码工作,“领航员”则负责检查,及时纠正代码中的问题。结对编程的形式使得代码处于不断地审查过程,每一段代码都由一个人编写,另一个人检查,最大程度上减少了出现bug的可能。在本次结对编程作业中,为了更好的体验驾驶员和领航员的角色,我和队友商量了一下后决定由队友写generate(),我写calc()。在写generate()时,队友担任驾驶员,我担任领航员,负责审查;在写calc()时,则相反。


 

calc()函数的一些想法

刚开始我打算先通过二叉树将中缀表达式转化为后缀表达式,然后用堆栈处理后缀表达式,由于题目要求所有的计算步骤中都不允许出现负数,所以可以通过二叉树的左右子树交换的方式来规避负数的出现(即出现负数就交换)。但是后来看了顾老师的课件(当时在复习二叉树)发现可以将中缀表达式直接通过堆栈的方式来计算(这里要求构造两个栈,一个存操作数,另一个存运算符)当时就感觉我应该好好复习数据结构了。言归正传,下面附上calc()的全部代码(篇幅限制没有给出里面函数的代码):

int calc(vector
&vs, vector
&vscal){ string str; vector
vstr; stack
OPND; stack
OPTR; for (int q = 0; q < vs.size(); q++) { str = vs[q]; splitstr(vstr, str); OPTR.push("#"); int i = 1; int k = 0; int sumsub = 0; int foundsub = 0; string op; string a, b, c; double doublea, doubleb, doublec; map
order1, order2; for (int j = 0; j < vstr.size(); j++) { order1[j] = j; order2[j] = j; } while (1) { if (!isoptr(vstr[i])) { OPND.push(vstr[i]); i++; continue; } else { while (1) { op = OPTR.top(); switch (cmp(op, vstr[i])) { case 0: b = OPND.top(); OPND.pop(); a = OPND.top(); OPND.pop(); OPTR.pop(); stringtodouble(b, doubleb); stringtodouble(a, doublea); if (op == "+") { doublec = doublea + doubleb; doubletostring(c, doublec); OPND.push(c); } if (op == "-") { sumsub++; doublec = doublea - doubleb; if (doublec < 0) { doublec = doubleb - doublea; for (k = 0; k < vstr.size(); k++) { if (vstr[k] == "-") { foundsub++; } if (foundsub == sumsub) { break; } } int k1 = k, k2 = k; k1--; k2++; while (vstr[k1] != "+" && vstr[k1] != "-" && vstr[k1] != "#") { k1--; } while (vstr[k2] != "+" && vstr[k2] != "-" && vstr[k2] != "#") { k2++; } int s; for (s = 1; s < k2 - k; s++) { order2[k1 + s] = order1[k + s]; } order2[k1 + s] = k; int t = k1 + s; for (s = 1; s < k - k1; s++) { order2[t + s] = order1[k1 + s]; } doubletostring(c, doublec); OPND.push(c); } else { doubletostring(c, doublec); OPND.push(c); } } if (op == "*") { doublec = doublea * doubleb; doubletostring(c, doublec); OPND.push(c); } if (op == "/") { doublec = doublea / doubleb; doubletostring(c, doublec); OPND.push(c); } break; case 1: OPTR.push(vstr[i]); break; case 2: OPTR.pop(); break; default: break; } if (cmp(op, vstr[i]) != 0) { break; } } } if (vstr[i] == "#" && OPTR.top() == "#") { break; } i++; } string ans; ans = OPND.top(); string anscal = ""; for (int i = 1; i < vstr.size() - 1; i++) { // cout << vstr[order2[i]]; anscal += vstr[order2[i]]; } anscal += "="; // cout << "="; anscal += ans; // cout << ans; vscal.push_back(anscal); vstr.clear(); while (!OPND.empty()) { OPND.pop(); } while (!OPTR.empty()) { OPTR.pop(); } } return 0;}

算法的思想如下:

堆.png-69.8kB
从上面的代码可以看出,我已经养成了一定的代码规范,如:

  • {}各占一行
  • 缩进,一般以4个空格
  • 行宽,不多于100个字符
  • 命名有一定的实际意思,诸如判断运算符的函数命名为isoptr()

这也算是学这门课的一个收获吧。


 

关于封装的一些想法

查了Google,数据封装是一种把数据和操作数据的函数捆绑在一起的机制,是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。对于本次实验来说我们要写很多函数,这有利于代码维护。但是我们只需提供一个接口,UI组不需要关心我们内部的具体结构,不需要实际细节,就能够使用我们的core。我们刚开始打算用dll封装,但是查了一些资料,以及在按照常规套路进行一番操作之后,发现并不能封装成DLL,编译器报错说是找不到文件(文件明明都在啊……),忙了一段时间之后还是不能解决,请教了以及封装好的组,还是解决不了。于是弃疗直接用头文件加上命名空间来与UI组对接,在对接过程中也出现了许多问题,比如:IDE不能识别加进去的头文件,解决办法:在IDE里添加新的空文件,然后将代码复制过去。(至今没有明白这是为啥)


 

关于接口的一些想法

有部分UI组使用内存操作的,另外的一些组是通过文件的方式操作,所以我们定义了两套接口,具体情况如下:

  • 通过文件操作:

    参数说明:
    numofques:算式总数
    numofopera:最多产生的运算符个数
    range:操作数范围
    accuracy:精度,取值范围为0~4,表示小数点后0~4位,超出范围取0或4
    zerodiv:是否支持整除,true表示支持整除,此时精度无效

    函数说明:

    GenerateNobrackets:产生不带括号的算式,结果需要另外计算
    Generatebrackets:产生带括号的算式,结果可以直接在产生时计算

    函数原型:

    void GenerateNobrackets(int numofques, int numofopera, int range, int accuracy, bool zerodiv);
    void Generatebrackets(int numofques, int numofopera, int range, int accuracy, bool zerodiv);

    调用函数后产生的算式存在result.txt,算式结果存在key.txt

  • 通过内存操作

    参数说明:
    numofques:算式总数
    numofopera:最多产生的运算符个数
    range:操作数范围
    accuracy:精度,取值范围为0~4,表示小数点后0~4位,超出范围取0或4
    zerodiv:是否支持整除,true表示支持整除,此时精度无效

    函数说明:

    GenerateNobrackets:产生不带括号的算式,结果需要另外计算
    Generatebrackets:产生带括号的算式,结果可以直接在产生时计算

    函数原型:

    void GenerateNobrackets(vector &vs, vector &vscal,int numofques, int numofopera, int range, int accuracy, bool zerodiv);
    void Generatebrackets(vector &vs, vector &vscal,int numofques, int numofopera, int range, int accuracy, bool zerodiv);

    调用函数后产生的算式存在vs里面,算式结果存在vscal里面


 

关于Debug的一些想法

Debug是一个老大难的问题,我本人也是十分畏惧Debug,对于一些编译不通过的bug还好说,但是对于一些运行中出现的error就十分的头疼,比如越界,非法访问内存地址。所幸这次是结对编程,队友很carry,有时候很低级的错误自己打死发现不了,但是队友能够一眼看出,这大大的提高Debug的效率,做到了优势互补。


 

关于对接

刚开始对接很不顺利,因为第一组是用文件进行操作的,而我们是用vector方式传递数据,这就很头疼,最后我觉得其它UI组也可能用文件进行操作,于是就添加了file版本,最终在我给出file版本后,那一组仅仅用了不到20分钟就对接完成(目前最快的一组不到十分钟就完成了,haha)。


 

关于本次结对编程的想法与收获

  1. 最大的收获就是一个良好的接口应该在一开始就和 UI组商量好, 例如给UI需要哪些数据,是用什么形式封装,这样封装会产生哪些问题,这些问题该怎么解决诸如此类。一个好的接口可以简化使用,更好的体现功能,也可以将内部的实现隐藏起来,保护程序不被修改,事半功倍。
  2. 结对编程能够很大的提高效率,特别是队友比较可靠的时候。
  3. 要多与UI组沟通,这个沟通不仅仅是刚开始时的沟通,而是在编程时,尽可能的多交流,这样才能及时发现对接上的一些问题,真正做到敏捷原则。

 

转载于:https://www.cnblogs.com/sanjing/p/8894177.html

你可能感兴趣的文章