结对项目阶段-模块化分析

  在个人项目中,我们完成了四则运算式的自动生成小程序,在结对项目中我们需要做的是把我们的程序按功能划分模块,将不同的功能模块完全分离,作为独立的部分进行测试。

  经过了软件工程理论课的学习,我们知道了判断一个程序设计好坏的标志是类的内聚性是否高,耦合度是否低,能够做到高内聚低耦合的设计才是我们希望看到的,这个时候,再回过头看看自己写的程序,简直想哭。当时写的程序用了树的结构,在递归生成运算表达式的时候顺便完成了表达式结果的求解,这不刚好和要求完全相反吗?这个程序除了获得用户输入,输出之外,其他的生成表达式,计算表达式都写在了一个模块中,完全不符合高内聚的要求。所以我们现在需要做的是将以前程序的主模块再按功能划分为两个模块,分别是自动生成表达式模块和根据表达式求解。

  对于自动生成表达式模块,我们现有的代码只需要经过简单的更改就能做到,主要的是如何根据表达式求解。因为我们一开始用的就是树结构,为了重用代码,我们的想法是先获得用户输入的表达式字符串,将此字符串转换成树结构。

  在具体完成表达式字符串转换前。我们还需要考虑的是操作数的多样性这个问题,程序得支持用户输入分数和整数,第一步要做的是将表达式字符串中的操作数字符串转换成相应的数据类型。(这个数据类型是个人项目中Value类型数据,私有变量是int numer,int demon,即分子分母)

具体代码:

void CharsToInt(char *tmpstr,int flag1, int flag2, int * numer, int * demon,int i)
{
    //根据flag1判断为带分数还是真分数
    int j;
    if (flag1 != 0)
    {
        //带分数中的整数部分
        char tmpstr1[10];
        int tmpint1,tmpint2,tmpint3;
        for (j = 0; j < flag1; j++)
        {
            tmpstr1[j] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        tmpint1 = atoi(tmpstr1);
        //带分数中的分子部分
        for (j = flag1+1 ; j < flag2; j++)
        {
            tmpstr1[j - flag1 - 1] = tmpstr[j];
        }
        tmpstr1[j - flag1 -1] = ‘\0‘;
        tmpint2 = atoi(tmpstr1);
        //带分数中的分母部分
        for (j = flag2+1; j < i; j++)
        {
            tmpstr1[j - flag2 -1] = tmpstr[j];
        }
        tmpstr1[j - flag2 -1] = ‘\0‘;
        tmpint3 = atoi(tmpstr1);
        *(numer) = tmpint1 * tmpint3 + tmpint2;
        *(demon) = tmpint2;
    }
    else
    {
        char tmpstr1[10];
        for (j = 0; j < flag2; j++)
        {
            tmpstr1[j] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        *(numer) = atoi(tmpstr1);
        for (j = flag2 + 1; j < i; j++)
        {
            tmpstr1[j - flag2 -1] = tmpstr[j];
        }
        tmpstr1[j] = ‘\0‘;
        *(demon) = atoi(tmpstr1);
    }
}

接下来,就是主要函数,由表达式字符串生成树,先上代码:

void Experssion::GenerateTree(string expr)
{
    stack <char> operStack;//运算符栈
    stack <Experssion *> dataStack;//数据栈
    expr += ‘\0‘;
    char tmpchar,c;
    int idx = 0;
    tmpchar = expr[idx++];
    Experssion * p;
    while (operStack.size() != 0 || tmpchar != ‘\0‘)
    {
        //如果不是运算符,则接收操作数,这个操作数可能为假分数,真分数,和整数。
        if (tmpchar != ‘\0‘ && !IsOper(tmpchar))
        {
            int flag1 = 0;//是否为假分数的标志,为0,不是;不为0,是假分数,且这个数值表示假分数中" ‘ "的位置
            int flag2 = 0;//是否为分数的标志,为0,不是;不为0,是分数,且这个数值表示分数中"/"的位置
            char tmpstr[10];//暂时保存操作数字符串
            int numer,demon;//操作数分子分母
            int i = 0;
            while (tmpchar != ‘\0‘ && !IsOper(tmpchar))
            {
                tmpstr[i] = tmpchar;
                if (tmpchar == ‘\‘‘)//如果字符为" ‘ "
                {
                    flag1 = i;
                }
                if (tmpchar == ‘/‘)
                {
                    flag2 = i;
                }
                tmpchar = expr[idx++];
                i++;
            }
            tmpstr[i] = ‘\0‘;
            //处理这个暂存的字符串,获得分子分母
            if (flag1 == 0 && flag2 == 0)//为整数
            {
                demon = 1;
                numer = atoi(tmpstr);
            }
            else//为分数
            {
                CharsToInt(tmpstr,flag1,flag2,&numer,&demon,i);
            }
            Value operation(numer,demon);
            p = new Experssion();
            p->Result = operation;
            dataStack.push(p);
        }
        else//读取的是运算符
        {
            switch(tmpchar)
            {
            case ‘(‘:
                operStack.push(‘(‘);
                tmpchar = expr[idx++];
                break;
            case ‘)‘:
                while(true)
                {
                    c = operStack.top();
                    operStack.pop();
                    if (c == ‘(‘)
                    {
                        break;
                    }
                    p = new Experssion();
                    p->oper = c;
                    if (dataStack.size())
                    {
                        p->right = dataStack.top();
                        dataStack.pop();
                    }
                    if (dataStack.size())
                    {
                        p->left = dataStack.top();
                        dataStack.pop();
                    }
                    dataStack.push(p);
                }
                tmpchar = expr[idx++];
                break;
            default:
                if (operStack.size() == 0 || tmpchar != ‘\0‘
                    && OperLevel(operStack.top()) < OperLevel(tmpchar))
                {//进栈
                    operStack.push(tmpchar);
                    tmpchar = expr[idx++];
                }
                else
                {//出栈
                    p = new Experssion();
                    p->oper = operStack.top();
                    if (dataStack.size())
                    {
                        p->right = dataStack.top();
                        dataStack.pop();
                    }
                    if (dataStack.size())
                    {
                        p->left = dataStack.top();
                        dataStack.pop();
                    }
                    dataStack.push(p);
                    operStack.pop();
                }
                break;
            }
        }
    }
    p = dataStack.top();
    dataStack.pop();
    this->oper = p->oper;
    this->left = p->left;
    this->right = p->right;
}

这个函数中主要用了两个栈,操作数栈和运算符栈。从表达式第一位开始读取字符,读到的不是运算符,则表明接下来读取的是操作数,操作数支持分数和整数。用一个比较复杂的例子解释:

比如操作数为11‘2/3,这是一个带分数,现在读取到的是‘1‘,根据这个子字符串得到操作数的分子numer,分母demon。在读取的过程中,用两个标志位表示符号 ’ 和符号 /的位置。然后用CharsToInt()将这个子字符串变成Value类型的操作数,即operation.numer = 35,operation.demon = 3.

  如果读取的是运算符,还要分运算符是否为括号。为左括号进栈,为右括号则脱括号。

最后根据生成的表达式数求解,代码如下:

//由生成的表达式树求解
Value Experssion::GetResult()
{
    if (left != NULL && right != NULL)
    {
        Value LResult = left->GetResult();
        Value RResult = right->GetResult();
        switch(oper)
        {
        case ‘+‘:
            Result = LResult + RResult;
            break;
        case ‘-‘:
            Result = LResult - RResult;
            break;
        case ‘*‘:
            Result = LResult * RResult;
            break;
        case ‘>‘:    //“>”代替除号
            Result = LResult / RResult;
            break;
        }
        return Result;
    }
    else
    {
        return Result;
    }
}
时间: 2024-10-23 00:19:44

结对项目阶段-模块化分析的相关文章

2015710301120/201571030119《小学四则运算练习软件》结对项目报告

一.项目源码 Github地址:https://github.com/myGitHub1018/Student_Comput3.git 二.项目报告 1.需求分析 (1)由计算机从题库文件中随机选择20道加减乘除混合算式,用户输入算式答案,程序检查答案是否正确,每道题正确计5分,错误不计分,20道题测试结束后给出测试总分: (2)题库文件可采用实验二的方式自动生成,也可以手工编辑生成,文本格式如下: (3)程序为用户提供三种进阶四则运算练习功能选择:百以内整数算式(必做).带括号算式.真分数算式

# 2019-2020-4 《Java 程序设计》结对项目总结

2019-2020-4 <Java 程序设计>结对项目阶段总结---<四则运算--整数> 一.需求分析 实现一个命令行程序 要求: 1.自动生成小学四则运算题目(加,减,乘,除): 2.支持整数: 3.支持多运算符(例如生成包含n个运算符的题目): 4.支持真分数(后续实现): 5.统计正确率. 扩展需求 1.文件: (1)处理生成题目并输出到文件: (2)完成题目后从文件读入并判断. 2.多语言支持:简体中文,繁體中文,English: 3.生成题目去重. 二.设计思路以及代码解

结对项目进展第二周——模块化分析

结对项目第一步:把实现的四则运算程序的功能划分模块,将不同模块功能分开,从而使模块可复用,并作为独立的部分进行测试. 优化的四则运算程序需要高内聚和低耦合.而我们组写得代码使用了树的数据结构,虽然表达起来简单易懂,算法也比较容易实现,却有着一个很大的缺点:我们在递归生成运算表达式的同事计算了表达式的结果.这样虽然算法和实现的代码都很简便,却不符合模块化的思想. 我们选择的原型程序,一共有输入.随机生成表达式框架(只有运算符但没有数据).在框架中填入数字并计算结果.输出共四个模块.实现的时候觉得很

对结对项目同伴的优缺点分析

上个星期完成了个人项目,这篇博客是对结对项目的伙伴的个人项目的优缺点的评价. 优点: 1.代码清晰明了,有相应的注释便于理解,函数命名规范易于看出该函数的功能,功能大致符合要求,没有多余且无用的参数,if.while等语句格式符合规范. 2.使用了多个自己写的头文件,将功能分隔开来使主程序容易阅读 3.使用了类的私有成员即账户,密码,初始登录等级,使这几项易于联系,便于观察. 4.使用了vector容器来存储文件数据,能够方便的进行随机存取, 由于vector容器提供额外的方法来增加.删除元素,

【SE】Week3 : 四则运算式生成评分工具Extension&amp;Release Version(结对项目)

Foreword 此次的结对项目终于告一段落,除了本身对软件开发的整体流程有了更深刻的了解外,更深刻的认识应该是结对编程对这一过程的促进作用. 在此想形式性但真心地啰嗦几句,十分感谢能端同学能够不厌其烦地接受我每次对软件的修改提议,并在代码实现过程中为团队贡献了许多人性化的tips: 另外,他积极好学的心态也很让我佩服.从初入面向对象,数据结构的使用,实际工程的开发,他快速地掌握了其中的技巧: 并在过程中不嫌辛苦地和我一起熬夜,才能在短短48h内高效利用时间,开发出这款颇多功能的软件.感谢!=)

结对项目-四则运算出题程序(GUI版)

目录: 一.致搭档(含项目地址) 二.PSP(planning) 三.结对编程中对接口的设计 四.计算模块接口的设计与实现过程 五.计算模块接口部分的性能改进 六.计算模块部分单元测试展示 七.计算模块部分异常处理说明 八.界面模块的详细设计过程 九.界面模块与计算模块的对接 十.结对过程的描述 十一.结对编程的优缺点 十二.PSP(actual) 一.致搭档:     队友:李庭娟(我很喜欢把这位队友叫娟子,感觉很亲切) 首先非常非常感谢这位结对搭档--娟子,从最初组队到如今合作,始终非常信任

结对项目—地铁出行路线规划

结对项目—地铁出行路线规划 我的搭档:陈鸿超 14061216 https://github.com/ChengFR/PairProgramming_SubwayRoute- 会在十一期间发布新版本 结对编程体会: 结对编程的优点: 站在软件开发的角度,两个人共同面对同一台电脑进行开发,无论是效率还是软件质量都要超过一个人进行开发的情况. 对于还处于学习阶段的年轻软件开发者来说,结对编程是一个很好的互相学习的机会 结对编程时动力.责任感更强 结对编程的缺点: 对于我们来说,寻找两个人共同的时间进

软工_结对项目总结博客

关于结对编程 第一次进行真正的结对编程,而且我们组又是最奇葩的三人组合(14061183韩青长)(14061195陈彦吉),在经历了三天的合作以后,感觉收获还是蛮多的,下面是我对于结对编程的一些个人体验. 优点 在结对编程的过程中,两个人共同面对同一份代码,编码时旁边时刻有人提示监督.这样写出的代码,首先考虑的特殊情况会更多,能避免很多一个人编程时因为考虑不周而在某个不起眼的地方产生的Bug,代码质量更高,少了很多调试时间. 同时,由于两个人交替工作,一方面可以缓解疲劳,同时又因为身旁有人共同工

2015结对项目

结对项目 作业截止日期:2015年10月6日  我们在第一个作业中,用各种语言实现了一个命令行的四则运算小程序.我们看看如果要把我们的小程序升级为能稳定运行,解决用户问题的软件,应该怎么做. 大家写了不少四则运算的练习,这些代码都各有特色,大家写的 “软件” 也有一定的用处.如果我们要把这个功能放到不同的环境中去(例如,命令行,Windows 图形界面程序,网页程序,手机App),就会碰到困难,因为目前代码的普遍问题是代码都散落在main ( )函数或者其他子函数中,我们很难把这些功能完整地剥离