结对编程收获

结对编程收获

  ——我和我的伙伴分到了Core组,他负责生成编写四则运算表达式函数,而我负责编写计算函数以及整体框架和接口

  这一次结对编程不仅学到了不少编程技术,更在交流合作中发现了自己以往的问题,因为我一直以来是一个偏爱独来独往的人,宁愿在合作中多干活,也不愿意别人对我的构想指手画脚,不过这一次是无法避免的了。

编程收获


1、实战经验

  这是一次难得的C++实战经验,或者说是一次面向对象语言的实践,C++更抽象一层的数据类型及其方法将我从以往繁重的体力活(用C语言编写)中解救出来,我也充分地利用了它不同于C的特性,将整个小项目的结构都编写得比较优美(至少是学习计算机语言以来)。(最后附上核心内部接口与外部接口)

2、开发流程

结队编程的效率=成员单独的效率-交流所带来的损耗,因此成员之间任务的耦合性越大,带来的损耗也越大,所以在合作的过程中,我有如下想法:

1)前期:最好采用和-分-和的讨论方法。

  (1)和:成员需要全部参与到代码整体脉络的讨论当中,然后尽量地以减小任务耦合度为原则分配任务。

  (2)分:当两者(或者几个人)任务存在可能的交叉时,成员需要单独地进行交流,对设计的版块进行细致讨论。(这一阶段非常重要,只有细致地讨论、设计才有助于尽早地发现BUG,这对开发周期有百利而难有一害)

  (3)和:然而两者(或者几个人)交流完毕后,不能急匆匆地开始开发,需要重新召开组会,交流各个版块的设计思想,并至少需要一个统领全局的人明确整体的脉络,发现中间可能存在的冲突、重叠,然后提供改进的建议。

2)开发:有几个需要注意的问题

  (1)如果数据结构存在重叠,即后者可能用到前者的数据结构时,那么他们的耦合度可以判定为高,需要共同设计代码、编写代码,并且开发时只有一人编写,而另一人需要实时观察、跟进。而两者(或者几个人)任务只是接口级别的,则可判定为低,需要事先约定好接口的类型,然后分别开发自己的模块,最后组建为一个类。

  (2)测试代码要跟进上每一个函数,尤其是项目越大这一点越是关键,虽然有一些函数因为参数表的问题难以编写测试类,然而并不能省略,因为之后的查错可能会把人逼疯。

3)归并:需要在线(最好是面对面)的交流

  在各个同学便写完代码需要归并在一起的时候,可以按照树的结构逐步上提,当然每一个节点都是一个更高层次的功能来确定的。这时候需要进行面对面的联合测试,因为这样在对接的时候能充分化解歧义,并且前后调用函数的编写人可以互相交流容易忽视的点,可能出现的异常。

3、开发周期:

  这次我充分吸收了上次个人编程时受到的教训,以逐次迭代的思想,我每开发一个新的模块时都列写出可能用到的最基本的函数,独立地进行编写、测试。事实上这样的经历是轻松+愉快的,在后期的调试中,至少没有在算法逻辑上遇到明显的问题。(当然C++这点没有Java做得好,Java的@Test注解对函数的测试是更加轻松的,也可能我学的C++太浅显了,毕竟从Java到C++的过渡只用了6个小时)

4、开发感想:

  读了其他同学的收获,我感觉我上文所述的方案有些许遗憾,因为我太在意去除耦合性,让每个人去做独立的工作,却忽略了两个人一起编程(人数更多可能就不管用了)所带来的其他效益(这里的一起是指同时做一件事情)。因为两个人独立思考一种算法的时候能够发现更多的漏洞,这里我也有提到,也就是最初第一和第三步的公共讨论部分;另外互相监督能促使队友更高效地工作,而不是时不时就去看一眼其他无关的内容;最后还能减少debug的时间,领航员对驾驶员的代码大致了解的时候可能有不同于你的固有认识,我想这个方面的确可能是两个人合作的效率大于单干效率之和的。

接口处理:


1、前后接口讨论的重要性:

  诚如上文所言,在最初的讨论中所有成员都应该参加,但是这次并没有邀请UI组(甚至最开始的时候还没有考虑到要联系UI组,因为接口函数看上去已经确定了)。也如后来UI同学在群里抱怨的,虽然逻辑上各组之间差异不大,但是他们最初想得太美好了,接口参数表、返回值、甚至编写语言的不同让UI组的工作一下子重了不少。我们在之后的第二天找到了UI组的同学,不过他们讨论的积极性也不高,所谓的对接组内讨论也不了了之。

2、人性化、例程:

  这一次我们的接口设计是比较成功的,因为据反馈有好几组UI都对我们的接口表示满意,我想这一方面得益于我在对接口设计时充分考虑到(a)尽可能少的调用函数完成尽可能多的事情(b)C语言仍然是所有同学熟练掌握的语言,并且它几乎成为各种编程语言会优先支持的外部语言;另一方面我也不厌其烦地在每一个版本中编写Test工程对dll进行测试,更对接口编写实际的使用例程并且不断进行更新,这样充分考虑用户感受使我们赢得了赞誉。

代码展现:


 

1、留给UI组的接口

extern "C" __declspec(dllexport)
void  SettingCal(int numOfOperand, int numOfQuestion, double rangeOfQuestion, bool addition, bool subtraction, bool multiplication, 
    bool division, bool power, bool brackets, int precision, bool properFraction, bool decimalFraction);
    
extern "C" __declspec(dllexport) 
void  GenerateAndCalc();

extern "C" __declspec(dllexport) 
const char*  getExpression(int count);

extern "C" __declspec(dllexport)
const char*  getAnswer(int count);

extern "C" __declspec(dllexport)
int numOfQuestion();
View Code

2、内部的代码框架

struct ReversePolishType {
    stack<double> OPTR;        //操作数栈
    stack<char> OPND;        //操作符栈
};

struct fractionType {//假分数形式
    int    numerator;            //分子
    int denominator;        //分母
};

struct RPT_FractionType {//真分数的逆波兰式表示
    stack<fractionType> OPTR;
    stack<char>    OPND;
};

class OperationClass
{
private:
//B同学:
    int numOfQuestion;        //题目的数量
    int numOfOperand;        //操作数的数量
    double rangeOfQuestion;    //题目中的数值范围

                            //运算符的种类
    bool addition;
    bool subtraction;
    bool multiplication;
    bool division;
    bool power;
    bool brackets;
    int precision;            //精度
    bool properFraction;    //是否支持真分数
    bool decimalFraction;    //是否支持小数
                            //都不支持说明支持整数
//计算整形、小数形式的算式
    static ReversePolishType ReversePolishNotation(string Input)throw(...);                        //计算整形、小数形式的算式:根据string生成逆序波兰式
    static double CalReversePolishNotation(ReversePolishType expression)throw(...);                //计算整形、小数形式的算式:根据逆波兰式计算结果

//计算分数形式的算式
    static RPT_FractionType RPN_FractionType(string Input)throw(...);                                //计算分数形式的算式:根据string生成逆序波兰式
    static fractionType CRPN_FractionType(RPT_FractionType expression)throw(...);                    //计算分数形式的算式:根据逆波兰式计算结果
                                                                                                            //分数形式的加、减、乘、除、乘方
    static fractionType fraAdd(fractionType a, fractionType b)throw(...);
    static fractionType fraSub(fractionType a, fractionType b)throw(...);
    static fractionType fraMul(fractionType a, fractionType b)throw(...);
    static fractionType fraDiv(fractionType a, fractionType b)throw(...);
    static fractionType fraPow(fractionType a, int b)throw(...);

//A同学:
    string generate()throw(...);
    string generate1()throw(...);
    string generate2()throw(...);
    string generate3()throw(...);
    string generate0()throw(...);
public:
    OperationClass();                //无参构造方法

//提供给UI的属性、方法
//对象的属性
    vector<string>    expression;        //存储生成的表达式
    vector<string>    answer;            //存储表达式的计算结果
    void Setting(int numOfOperand, int numOfQuestion, double rangeOfQuestion, bool addition, bool subtraction, bool multiplication, 
        bool division, bool power, bool brackets, int precision, bool properFraction, bool decimalFraction);/*设置属性:操作数数量,
    题目数量,数值范围,加法?减法?乘法?除法?乘方?(真分数?or小数?最后两项只可选其一,否则抛出异常)*/
    void GenerateAndCalc();
    void Generate();                //生成表达式,并存储在string当中
    void CalcAllExpression();        //对题目表达式依次进行计算,将结果string数组返回
                                    //获得对象的属性
    int getNumOfQuestion();            //获取题目数量设定值


//B同学提供给A同学的方法
//计算表达式expression
//返回double类型的答案
    double CalcDouble(string expression)throw(...);                    //整数/小数形式表达式:对输入表达式字符串进行运算,然后以数值形式返回
    double CalcFrationDouble(string expression)throw(...);            //分数形式表达式:对输入表达式字符串进行运算,然后以数值形式返回
                                                            //返回string类型的答案
    string Calc(string expression)throw(...);                            //整数/小数形式表达式:对输入表达式字符串进行运算,然后以数值形式返回
    string CalcFration(string expression)throw(...);                    //分数形式表达式:对输入表达式字符串进行运算,然后以数值形式返回

};
View Code
原文地址:https://www.cnblogs.com/Trinidad/p/8893597.html