软件工程结对作业

软件工程结对作业

——四则运算

 

School of information,USTC

A同学:李浩冉 PB16061409

B同学:曾子轩 PB16061449

一、任务流程(作业要求:http://www.cnblogs.com/silent-zlv/p/8684979.html)(GitHub:https://github.com/TrinidadZZX/GroupWork)

  (1)项目摘要:写一个根据设定要求能自动生成小学四则运算题目并进行计算的DLL库。

  (2)开发计划:

  1)讨论流程:按照用户的需求,我们打算将流程设计为:设定属性->生成表达式、计算结果->UI调用。

  2)分配任务:经过对用户需求的分析,以及对任务量的权衡,我们将核心算法分为两个部分中缀表达式生成表达式计算,并将它们合并起来写为一个函数作为对UI的接口。又因为用户的需求最初是偏、难、怪的,所以将最难的中缀表达式生成部分和相对容易的完善代码框架、外部接口、表达式计算分为两个任务交给A、B两个同学。

  3)分析实现方法:

    在生成部分采用随机函数,根据整数,分数与小数其特点生成字符串,并返回。

    在计算部分采用先将表达式转化为逆波兰式,然后再利用栈结构进行计算。

    因为两个部分没有结构上的重合,更深层次地是为了更快地完成开发,所以约定好以字符串作为两者的接口后便开始了并行开发。

  (3)时间规划:

  A同学:

                  流程规划

计划用时

实际用时

 

前期讨论

分析,讨论任务实现流程

30min

 

1h

分析,讨论任务难点并分配任务

30min

分析代码耦合性,讨论参数传递

15min

前期准备

查阅随机函数,学习代码规范

1h

     1h

框架设计

学习代码框架,确定抽象函数接口

30min

     1h

 

具体实现

用随机函数产生随机数(保证随机性)

写出产生整数式子的函数

类比写出分数,小数(结构统一)

局部优化(式中不出现负数,整数操作符两边加空格)

1h

3h

2h

4h

 

 

5h

整合

与B同学的程序进行整合修改

2h

     1h

  B同学:

  

  (4)代码架构和设计:

    在多人任务中宜采用面向对象的语言,本例中采用的C++促使开发者以更明晰的结构思考代码接口,对后期的测试、维护很有帮助(事实上Java在测试方面更胜一筹,然而最初任务中规定以头文件的形式与UI进行交互,所以也就选择了与C兼容的C++)

    代码风格展现:(OprationClass.h)

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(...);
private域
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(...);                    //分数形式表达式:对输入表达式字符串进行运算,然后以数值形式返回
public域

 

  (5)调试与优化

    A同学:

  1)调试

   由来:在表达式生成的过程中出现了多余的括号(在一个数字的两端有括号)

   发现:发现bug 后,我首先,在函数里找到生成括号的两个部分,(brackets==1即有括号时,与减法),由于算法所以brackets==1时不出生成多余的括号,所以只有可能是减法部分出错。

减法思路,如果做左操作数(可以是数字也可能是式子)比又操作数小,交换左右操作数并给现在的右操作数加上括号,当时没有考虑到,右操作数可能只是一个数字,所以加上括号后就产生了多余的括号。

   解决:方法很简单,交换后的右操作数是数字只有可能是生成的第一个操作上数时,所以加上if语句判断当前是否为第一个操作数,如果是不加括号交换。

   思考:bug 很好查出来,但是这很好的说明了在写代码时的考虑不周,幸亏进行了近千词的生成,否则根本无法察觉。

  2)优化

    其实在第一个版本也就是光生成表达式没有加括号和对表达式中出现负数的处理,分析表示整体费时很少,但是加上哪些功能后,加上了一堆判断函数,和一堆操作同时减法又调用了B同学的计算函数,耗时呈指数形式增加,由于B同学熟练使用c++,于是我请教他用string的方法处理字符串,将我最初的一段字符串处理改为string,发现时间减少了,但是由于时间关系,没有将全部代码改成string型。

    B同学:

  1)调试:一个算法逻辑上的小BUG,一个机器精度上的大BUG

   算法逻辑(20min解决):

     由来:在进行分式的运算的时候,需要对分式进行通分,而在求取最大公约数的过程中:

//求取最大公因数
inline int commonDivisor(int a, int b)
{//最大公约数
    int temp1, temp2, temp3;
    if (a1 >= b1)
    {
        temp1 = a1;
        temp2 = b1;
    }
    else
    {
        temp1 = b1;
        temp2 = a1;
    }                    //通过比较对num1和num2赋值,便于计算
    while (temp2>0)
    {
        temp3 = temp1 % temp2;
        temp1 = temp2;
        temp2 = temp3;
    }                  //辗转相除,num2=0时,num1=最大公因数
    return temp1;
}
commonDivisor

      可见如果输入的值存在负数,那么算出的结果将是0,这也是前期总是出现除零错误的原因。

     发现:在出现除零错误后,我在程序中加入断点,观察对什么样式的表达式会出现错误,然后选取其中之一代入运行,结果发现在求取最大公约数的函数中发生错误。

     解决:很简单,只需要在函数的开头加入取绝对值的语句即可。

     思考:发现、解决一个BUG很简单,但是一个BUG的解决往往会带来一系列的问题,因为这引出了一整类的你没有考虑到的问题,需要对调用函数甚至整个流程进行重新考虑。这一次比较幸运的是只需要在调用函数中增加一点判断,然而当调用函数不止一个或者是在整个流程出现漏洞的时候,这个问题就很严重了。所以BUG的发现是越早越好,这也是说BUG在越早的时间被考虑到,它所带来的负面效应则越小。

   机器精度(4h解决):

     由来:随机生成的表达式在计算过程中可能存在溢出现象,而溢出后将出现一系列奇怪的错误。

     发现:尽管算法在逻辑上并没有问题(对于操作数较小的算式在有限次测试中没有发生问题),但是操作数一大,各种问题都涌现出来。

     解决:实际上到现在都难以理解为什么溢出后在计算中会出现除零错误,尽管我在表达式中声明了对各种异常的接收,但是程序仍然对一些异常(例如除零)没有进行处理,没有办法:一方面我在各个底层的计算函数的声明上加入了throw(...)允许抛出各类异常,然后触发异常后重新生成算式,另一方面对操作数的大小进行了限制。

     思考:我现在意识到了机器精度所引发的问题,尤其是int型很容易触发(至少在本例中int比float型因数值范围更容易触发异常),以后一方面我在数据类型的选择上将通过宏定义进行限定,另一方面我会在计算机领域更深入地探讨这个问题。现在虽然逻辑上自己能做到几百行只出现一两个BUG,但是能无法解决因为精度而引发的一系列BUG。

    2)优化

    

    判断:这次作业中,我采用了很多C++固有的数据类型,例如string、stack。虽然这简化了编程,但是在优化上能做的却非常少,因为这相当于你站在别人搭的戏台上表演,然而最后你需要对戏台进行检查、优化,这对我而言很难做到。另一方面本例的核心函数(重要又耗时间)集中在A同学的生成表达式函数中,而他的函数又调用了我写的函数,能做的优化也十分有限。

    思考:针对以上所遇到的问题,以后在对效率有苛刻要求的地方,我尽量采用面向过程的C语言进行编写,这样更容易地控制底层实现,甚至可以修改汇编指令,以达到更优的效率。

、个人作业与结队编程的差异分析

  A同学:

    对于个人作业来说代码是给自己看的,只要自己懂就行了,但是如果要和别人结对编程,一定要先进行代码规范,这样子别人不至于完全看不懂你的代码,而且事先要搞清楚自己的函数名,要返回什么,返回什么类型,像这次变成我就做的不太好,由于用c 语言比较多,我产生的函数返回的是char 型指针,而B同学需要的是string类型的一条字符串,我以为这两者是互通的,结果发现并不是,最后费了一些时间将我的代码更改为返回值为string 的函数,才将代码融合成功

 

  B同学:结队编程的效率=成员单独的效率-交流所带来的损耗。成员之间任务的耦合性越大,带来的损耗也越大,为了降低损耗,我谈一谈在实际编程中得到的经验:

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

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

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

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

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

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

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

三、对未来的思考:走上工作岗位后,是否选择结对编程用于解决部分任务?

  A同学:

  结对编程我认为是必须的,除非能强到自己一个人开发一个项目,当然这是不可能的,起码对于我来说。所以对于我来说,掌握代码规范是一件非常重要的事情,因为不可能让团队里的其他人来配合我一个人,让他们跟上我的习惯,所以我尽可能的要去和其他人一致,而一般的好的程序员,它们的程序都是相当规范的,所以说我平日里写代码,要尽量规范自己。

  B同学:

  走上工作岗位后,除非我异常杰出(至少现在看来是不存在的),否则必然会参与结队编程。当然对于队伍也是需要明智的选择,因为一个项目的成败将很大地影响一个程序员的声誉,至少对于新手程序员是这样的。一方面选择队伍很关键,一个有凝聚力的队伍对项目的进展速度、成员之间的交流都很有裨益,这也往往决定于项目经理的能力,而选择队友也很关键,你需要选择的可能就是那些能熟练运用团队开发软件协助开发、掌握并能熟练运用多门语言,并能和你流畅交流的人,否则交流、学习的损耗将占据很大一部分开发时间。

四、对课程的看法

  A同学:

  这么课对我的抗压能力有着很大的帮助,其实对我来说过程是痛苦的,但是结果一定是快乐的,因为我学到了许多的知识,可能这些东西需要几个学期去学习,可在一个很短的时间里能够学到,还是非常满足的。而且这门课让我读了许多书,让我了解到许多的程序员的成长经历,我希望老师可以能请到一些像书中那样的程序员,来与我们面对面分享一些他们的故事,这比我们看书应该能收获的更多。

  B同学:

  这一门课程对于我的帮助是精神、实际能力两个方面的:

    精神:在繁重的学习生活之余,还要抽出很多的时间完成软工作业,我体验到了人的毅力可能是永无止境的(好吧,如果再加量恐怕就要死了TAT),而在望着一堆渐渐被改烂的代码,我不再是仅仅绝望地望着屏幕,而是稍作休整,继续前行。

    能力:1)有一次实际的编程机会让我浅浅领会C++编程思想,实际经验在计算机领域是很重要的,因为看再多遍的诸如《C++语言程序设计》也比不上实地地按照书上的思想从头到尾地编写一次代码。

       2)能促使我在更短地时间内去掌握一门技术(因为不抓紧恐怕就死了)。

posted @ 2018-04-15 23:47  Trinidad_ZZX  阅读(296)  评论(2编辑  收藏  举报