结对编程收获

 
 

结对编程收获

沈三景 软件工程 结对编程


 

前言

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


 

结对编程

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


 

calc()函数的一些想法

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

int calc(vector<string> &vs, vector<string> &vscal)
{
    string str;
    vector<string> vstr;
    stack<string> OPND;
    stack<string> 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<int, int> 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