小学四则运算结对项目报告【GUI】

结对项目报告【GUI】


 

博客一览


 

一、仓库地址

二、预计PSP

三、结对编程对接口的设计

四、计算模块接口的设计与实现过程

五、计算模块接口部分的性能改进

六、计算模块部分单元测试展示

七、计算模块部分异常处理说明

八、界面模块的详细设计过程

九、界面模块与计算模块的对接

十、结对过程的描述

十一、结对编程的优缺点

十二、完成后实际的PSP

十三、项目总结与改进


 

一、仓库地址

代码仓库地址:https://git.coding.net/Siamese_miao/team.git

二、预计PSP

PSP

任务内容

计划共完成需要的时间(h)

Planning

计划

0.5

Estimate

   估计这个任务需要多少时间,并规划大致工作步骤

0.5

Development

开发

39.25

   Analysis

需求分析 (包括学习新技术)

0.5

Design Spec

  生成设计文档

0.25

  Design Review

设计复审 (和同事审核设计文档)

0.25

Coding Standard

 代码规范 (为目前的开发制定合适的规范)

0.25

 Design

 具体设计

2

Coding

具体编码

30

Code Review

 代码复审

1

Test

测试(自我测试,修改代码,提交修改)

5

Reporting

报告

4

Test Report

测试报告(包括博客)

3

 Size Measurement

计算工作量

0.5

Postmortem & Process Improvement Plan

事后总结, 并提出过程改进计划

0.5

三、结对编程对接口的设计

1、在信息隐藏方面

Information hiding is part of the foundation of both structured design and object-oriented design. In structured design, the notion of “black boxes” comes from information hiding. In object-oriented design, it gives rise to the concepts of encapsulation and modularity, and it is associated with the concept of abstraction.

----From Code Complete Section 5.3

在《代码大全》中列出了两类需要隐藏的内容:

第一类信息是复杂化的信息,对于我们的项目而言,对于出题的用户来讲,他不需要知道程序在收到需求后是如何运行的,而对于做题的用户来讲,他不需要知道程序是如何来判题的,信息隐藏将内部的算法实现封装起来,对外部只提供调用的接口,使得程序之间各个模块各司其职,互不影响。

对于第二类,是指变动的信息,比如在用户的输入需求中出现了错误并被抛出,这个错误在类中进行了适当的处理,错误没有扩散,这样可以提高程序的容错性

2、在接口设计方面

在接口的设计过程中,我们尽量贴合实际需求并要求接口避免冗余,接口与接口之间互相独立,使用方便。

3、在松耦合方面

所谓耦合性指的是两个子程序之间联系的紧密程度,子程序之间具有良好耦合的特点是它们之间的耦合是非常松散的,任一个子程序都能很容易地被其它子程序调用,在我们的程序中,每一项功能有独自的存在类,多个类之间可以轻易调用,使用之间互不干扰。整个项目中不存在不合理耦合,使用简单数据耦合与控制耦合。

四、计算模块接口的设计与实现过程

1、 类的设计

(1)Command类:对命令行参数进行分析调用

(2)createFile类:生成对应的文本文件

(3)formula类:根据需求生成对应的算式

(4)calculate类:对算式进行相应的计算

(5)stack类:用于算式计算

2、类中的方法设计

(1)  Command类:

       a:isInteger(String s)

              判断字符串是否为数字

(2)  createFile类

       a:   fileCreate(int n,int lower,int upper,int o,int c,int b)

              根据参数生成符合要求的文件

(3)  formula类:

       a: Bracket_AS(int lower,int upper,int o)

              产生有括号的加减算式,调用decide1(),decide(),work();

       b: Bracket(int lower,int upper,int o)

              产生有括号的四则运算式,调用numberB(),work(),operator();

       c: AddSubtract(int lower,int upper,int o)

              产生简单加减算式,调用decide1(),decide();

       d: Arithmetic(int lower,int upper,int o)

              产生四则运算式,调用numberA(),operator(),work();

(4)  calculate类

       a: numberA(int key,int num,int result,int lower,int upper)

              产生无括号情况下的数

       b: numberB(int key,int num,int result,int lower,int upper)

              产生有括号情况下的数

       c: decide0(int x,int min,int max)

              产生加数

       d: decide1(int x,int min,int max)

              产生减数

       e: decide2(int x,int min,int max)

              产生除数

       f: decide3(int x,int min,int max)

              产生乘数

       g: operator(int num,int middle2,int middle3)

              限制乘除法出现条件

(5)  stack类

       a: work(String s,int lower,int upper,int op)

              由中缀表达式转为后缀表达式

       b: doCal(int a,int b,String stmp)

              判断弹出的符号并计算

3、流程图

 

五、计算模块接口部分的性能改进

 基于原来的个人项目代码,由于出现了运算过程以及运算结果数值范围的限制,原本的result(String temp)不再使用,改用了栈运算。

 

// 计算结果
    public static Object result(String temp) {
        ScriptEngineManager sem = new ScriptEngineManager();
        ScriptEngine se = sem.getEngineByName("js");
        Object last = 0;
        try {
            last = se.eval(temp);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return last;
    }

在栈的运算中加入判断

if (Math.abs(sresulat) > upper || Math.abs(sresulat) < lower) {
                                return 0;
                        }

另外,原本的操作符是一开始随机生成好的再判断选择后一个数,然后再判断符号是否合法,再修改符号,如果还是有小数或负数,则重新运行生成算式的函数,这样使得代码运行有些慢且多次运行。再加上数值范围的限定以及可以存在负数,我改变了想法。

因为负数的存在,使得加减号并没有数字的限制,而乘法有上限限制,除法有下限限制。所以在只有加减的运算中,符号随机生成,后一个数根据运算符以及数值范围生成合法的数。

// 相加不超过范围
    public static int decide0(int x, int min, int max)
    {
        int y;
        int temp = 0;
        if (x > 0)
        {
            temp = max - min - x + 1;// 加一个正整数范围
        } else
        {
            temp = max - (min - x) + 1;// 加至正整数的范围
        }
        if (temp < 0)
        {// 范围小于0
            if (x > 0)
            {
                temp = Math.abs(x) - min * 2 + 1;// 正整数过大,需加负数
                y = 0 - (int) (Math.random() * temp) - min;
            } else
            {
                temp = Math.abs(x) - 2 * min + 1;// 负数过小,越值,加小整数至负数范围
                y = (int) (Math.random() * temp) + min;
            }
        } else
        {
            y = (int) (Math.random() * temp + min);
        }
        return y;
    }

    // 相减不小于最小
    public static int decide1(int x, int min, int max)
    {
        int temp = 0;
        int y = 0;
        if (x > 0)
        {
            temp = x - 2 * (min - 1) - 1; // 减一个正数范围
        } else
        {
            temp = max + x - min + 1;// 减一个正数范围
        }
        if (temp > 0)
        {
            if (x < 0 && temp < min)
            {
                temp = Math.abs(x) - 2 * min + 1;// 负数过小,需减负数
                y = 0 - (int) (Math.random() * temp) - min;
            } else
            {
                y = (int) (Math.random() * temp + min);
            }
        } else
        {
            temp = max - x - min + 1;// 只有x>0的情况会出现,正数过小,需减负数
            y = 0 - (int) (Math.random() * temp) - min;
        }
        return y;
    }

当有乘除时,则根据上一个数生成操作符,再根据操作符生成合法的后一位数。

// 操作符的选定
    public static int operator(int num, int middle2, int middle3)
    {
        if (Math.abs(num) <= middle2)
        {// 除法下界
            if (Math.abs(num) < middle3)
            {
                return 3;
            } else
            {
                return 0;
            }
        } else if (Math.abs(num) >= middle3)
        {// 乘法上界
            return 2;
        } else
        {
            return (int) (Math.random() * 4);
        }
    }
// 下一位数字的选定
    public static int[] numberB(int key, int num, int lower, int upper)
    {
        int[] find = new int[] { 0, lower };
        if (key == 0)
        {
            find[1] = decide0(num, lower, upper);
            return find;
        } else if (key == 2)
        {
            int[] judge = new int[2];
            judge = decide2(num, lower);// 确保能够整除,并不低于下限
            if (judge[0] == 0)
            {
                find[1] = judge[1];
                return find;
            } else
            {
                find[0] = 1;
            }
        } else if (key == 3)
        {
            find[1] = decide3(num, lower, upper);
            if (find[0] == 0)
            {
                return find; // 乘法不超过上限
            }
        }
        find[1] = decide1(num, lower, upper);
        return find;
    }

这样大大减少了重新调用函数的问题,并且实现了运算过程与数值皆在范围内的功能。

其中,生成有括号与乘除的式子生成的函数判断耗时最多,因为它的判断较多,限制较多,优先级易改变,容易生成最终不合法的式子而重新运行。

// 带括号的四则运算
    public static String Bracket(int lower, int upper, int o) {
        int middle2 = lower * lower;// 除法下界
        int middle3 = upper / lower;// 乘法上界
        int brack_left = 0; // 记录未匹配的左括号个数
        int brack = 0; // 括号个数
        int j = 0;
        char[] p = new char[] { '+', '-', '÷', '*' };
        String temp1 = "";
        int[] num = new int[o + 1]; // 数字
        int[] key = new int[o]; // 符号所在的下标
        num[0] = (int) (Math.random() * (upper - lower + 1) + lower);
        int result;
        int[] find = new int[2];
        for (j = 0; j < (o - 1); j++) {
            if (num[j] < 0) {
                temp1 += "(" + String.valueOf(num[j]) + ")";
            } else {
                temp1 += String.valueOf(num[j]);
            }
            int tmpcnt = brack_left;
            for (int i = 0; i < tmpcnt; i++) { // 若当前有未匹配的左括号,则对每一个未匹配的左括号,都有一定概率生成相应右括号。
                if ((int) (Math.random() * 5) > 1) { // 生成右括号概率为0.6
                    brack_left--;
                    temp1 += ")";
                }
            }
            key[j] = calculate.operator(num[j], middle2, middle3);
            find = calculate.numberB(key[j], num[j], lower, upper);
            if (find[0] == 1) {
                key[j] = 1;
            }
            num[j + 1] = find[1];
            temp1 += String.valueOf(p[key[j]]);
            if (((brack * 2) <= o) && (((int) (Math.random() * 2)) == 0)) { // 以一定概率生成左括号,概率为1/2
                temp1 += "(";
                brack++;
                brack_left++;
                j++;
                if (num[j] < 0) {
                    temp1 += "(" + String.valueOf(num[j]) + ")";
                } else {
                    temp1 += String.valueOf(num[j]);
                } // 生成左括号后必须生成一个数字和运算符,不然可能出现(15)这样的错误
                key[j] = calculate.operator(num[j], middle2, middle3);
                find = calculate.numberB(key[j], num[j], lower, upper);
                if (find[0] == 1) {
                    key[j] = 1;
                }
                num[j + 1] = find[1];
                temp1 += p[key[j]];
            }
        }
        while (j != o) { // 判断是否为最后一个数
            if (num[j] < 0) {
                temp1 += "(" + String.valueOf(num[j]) + ")";
            } else {
                temp1 += String.valueOf(num[j]);
            }
            key[j] = calculate.operator(num[j], middle2, middle3);
            temp1 += p[key[j]];
            find = calculate.numberB(key[j], num[j], lower, upper);
            if (find[0] == 1) {
                key[j] = 1;
            }
            j++;
            num[j] = find[1];
        }
        if (num[o] < 0) {
            temp1 += "(" + String.valueOf(num[o]) + ")";
        } else {
            temp1 += String.valueOf(num[o]);
        }
        while ((brack_left) != 0) { // 补全右括号
            temp1 += ")";
            brack_left--;
        }
        result = stack.work(temp1, lower, upper, 1);
        if (result == 0) {
            temp1 = Bracket(lower, upper, o);
        }
        return temp1;

    }

}

在附加题记录用户模块,一开始使用contains(name)函数判断用户,后来发现这样会出现abc与abcabc被认为同一个人而的情况,经过思考,我们使用字符串的断开。
String[] arrays = txt.split(" ");
在使用equals(String)函数判断用户,解决了这个问题。

2、性能分析展示

项目总体分析图,从内存,多线程,CPU等方面分析了计算模块的性能,截图如下

性能分析过程截图:

 

 

 

六、计算模块部分单元测试展示

 

@Test
    public void testWork() {
        assertEquals(0, stack.work("7-5÷(1*37)÷(1*83)", 1, 900, 1));
        assertEquals(30, stack.work("55+(-25)÷5*(20-15)", 2, 300, 1));
        assertEquals(80, stack.work("((55+25)÷5)*(20-15)", 2, 300, 1));
        assertEquals(0, stack.work("60*(20-15)", 2, 200, 1));
    }

该部分测试的是栈的运算。

第一个断言测试的是无法整除返回错误标志0;

第二个断言测试的是负数运算;

第三个断言测试的是特殊括号位置的运算;

第四个断言测试的是超过数值返回错误标志0。

@Test
    public void testAll() {
        // 顺序不同以及异常测试。生成的文件会被覆盖。
        String[] arg0 = new String[] { "-n", "100", "-m", "5", "100", "-o", "3", "-c", "-b" };
        String[] arg1 = new String[] { "-m", "5", "50", "-o", "3", "-n", "100", "-c" };
        String[] arg2 = new String[] { "-o", "3", "-m", "5", "50", "-n", "100", "-b" };
        String[] arg3 = new String[] { "-n", "100", "-o", "3", "-m", "5", "50" }; 
        Command.main(arg0);// 有括号四则运算测试
        Command.main(arg1);// 四则运算测试
        Command.main(arg2);// 有括号加减运算测试
        Command.main(arg3);// 加减运算测试
}

该部分测试的命令行的更改输入顺序的四种出题选择正常运行。输入异常部分请看第七点。

@Test
    public void testDecide2() {
        int[] find = new int[2];
        find = calculate.decide2(20, 2);
        assertEquals(2, find[1]);
        find = calculate.decide2(13, 2);
        assertEquals(1, find[0]);
    }
decide2(int x, int min)为除法选择除数的函数,函数如下:
// 被除数能被除数整除并不低于最小
    public static int[] decide2(int x, int min)
    {
        int[] judge = new int[] { 1, 0 };
        int temp = Math.abs(x) / min - min + 1;// 除数的范围
        for (int i = min; i < (temp + min); i++)
        {
            if (Math.abs(x) % i == 0)
            {// 判断是否整除
                judge[0] = 0;
                judge[1] = i;
                return judge;
            }
        }
        return judge;
    }

其中,judge[0]用于判断该数能否有可整除的除数,1为没有,0为有,judge[1]为除数的值。该单元测试则测试了一次可产生除数与一次不能产生除数的情况。

七、计算模块部分异常处理说明

@Test
    public void testAll() {
        String[] arg4 = new String[] { "-o", "3", "-m", "5", "50", "-n" };
        String[] arg4_1 = new String[] { "-o", "3", "-n", "-m", "5", "50" };
        String[] arg4_2 = new String[] { "-n", "100000", "-m", "5", "50" };
        String[] arg4_3 = new String[] { "-o", "3", "-m", "5", "50" };

        String[] arg5 = new String[] { "-n", "50" };
        String[] arg5_1 = new String[] { "-m", "5", "-n", "50", "-o", "3" };
        String[] arg5_2 = new String[] { "-n", "50", "-m", "3" };
        String[] arg5_3 = new String[] { "-n", "50", "-o", "3", "-m" };
        String[] arg5_4 = new String[] { "-m", "-n", "50" };

        String[] arg6 = new String[] { "-o", "11", "-m", "5", "50", "-n", "100" };
        String[] arg6_1 = new String[] { "-n", "100", "-o", "-m", "5", "50" };
        String[] arg6_2 = new String[] { "-n", "100", "-m", "5", "50", "-o" };

        String[] arg7 = new String[] { "-m", "5", "20", "-n", "100", "-c" };
        String[] arg7_1 = new String[] { "-m", "5", "50", "-n", "100", "-b" };

        String[] arg8 = new String[] { "-b", "1", "-o", "3", "-m", "5", "50", "-n", "100" };
        String[] arg8_1 = new String[] { "-c", "1", "-o", "3", "-m", "5", "50", "-n", "100" };
        String[] arg8_2 = new String[] { "-n", "100", "-m", "5", "50", "-d" };
Command.main(arg4);// 缺少题数值测试
        Command.main(arg4_1);
        Command.main(arg4_2);// 题数值过大测试
        Command.main(arg4_3);// 缺少题数测试

        Command.main(arg5);// 缺少数值范围
        Command.main(arg5_1);// 缺少数值范围上限测试
        Command.main(arg5_2);
        Command.main(arg5_3);// 缺少数值范围上下限测试
        Command.main(arg5_4);

        Command.main(arg6);// 操作符数值过大测试
        Command.main(arg6_1);// 缺少操作符数值测试
        Command.main(arg6_2);

        Command.main(arg7);// 乘除需要上界大于下界的平方
        Command.main(arg7_1);// 括号需要操作符数大于1

        Command.main(arg8);// 输入非法测试之b后有数字
        Command.main(arg8_1);// 输入非法测试之c后有数字
        Command.main(arg8_2);// 输入非法测试之无辨识字符
    }
  1. 缺少题数值(-n后无带数字,如arg4与arg4_1)时,提醒缺少题数值,并告知-n的范围;
  2. 题数值过大(-n后数值超过10000,如arg4_2)时,提醒告知题数值范围(过小同理);
  3. 缺少题数(命令中无-n,如arg4_3)时,提醒-n为必须项,并告知-n范围。
  4. 缺少数值范围(命令中无-m,如arg5)时,提醒-m为必须项,并告知-m上下限各自范围;
  5. 缺少数值范围上限(-m后只带一个数字,如arg5_1和 arg5_2)时,提醒缺少上限,并告知上限范围;
  6. 缺少数值范围上下限(-m后不带数字,如arg5_3和 arg5_4)时,提醒缺少上下限,并告知上下限各自范围。
  7. 操作符数值过大(-o后数值超过10,如arg6)时,提醒告知操作符数值范围(过小同理);
  8. 缺少操作符数值(输入-o,后方没有带数值,如arg6_1与arg6_2)时,提醒缺少操作符数值,并告知-o范围。
  9. 选择乘除法但是上界小于下界的平方,无法生成含有乘除的式子(如arg7)时,提醒上界需大于下界的平方;
  10. 选择括号但是操作符默认为1或选择为1,不符合生成括号的条件(如arg7_1)时,提醒选择括号需要操作符数大于1。
  11. –b(或-c)后带数字(如arg8与arg8_1),提醒-b(或-c)后不能带数字;
  12. 出现除m、n、o、b、c外的字符如d等(如arg8_2),提醒输入值非法

八、界面模块的详细设计过程

1、初始设计

       初始界面出题做题单选,出题界面选择需求,做题界面先输入用户名将题目一行一行读出,填入答案后才可以跳转下一题,最后给出做题情况以及该用户的历史得分

2、设计框图

 

 

 

3、具体实现

 1、出题做题选择界面

 

2、选择出题界面

 

选中特定框时有提示输入要求

 

3、出题成果

 

4、做题用户名界面

 

 

5、选择题目文件界面,限制txt文件

 

6、题目上传成功并开始计时页面

 

7、做题界面

 

8、用户第一次做题界面

 

 

9、用户再次做题界面

 

 

九、界面模块与计算模块的对接 

1、对接项

        (1)出题模式

              获取参数后调用类出题创建文件

        (2)做题模式

              获取用户姓名后,执行上传文件类,读取后进行做题操作输出结果

 2、流程图

 

 

十、结对过程的描述

1、描述

我们两个人在结对过程中不断切换驾驶员领航员角色,对不同的性能需求进行分板块进行,比较顺利的完成了不同板块的对接与项目的测试。完成了整个项目的性能分析和单元测试的覆盖率分析。

 2、结对照片展示

十一、结对编程的优缺点 

1、结对编程优缺点

(1)优点

互相帮助、互相学习、能力上得到互补

可以增强代码和产品质量,有效地减少bug

在编程中,相互讨论,可能更快有效地解决问题

有两台电脑可以测试程序效果

(2)缺点:

对于有不同习惯的编程人员,可能在一起工作会产生一些麻烦,甚至矛盾。

结对编程可能让程序员们相互学习得更快,但有些时候可能会滋生不良氛围

有经验的人可能更喜欢单独作业,结对会使他的状态受到影响

 2、结对个人优缺点

庄莉

       优点:

    认真细心,有责任心

    代码能力高

    动手能力强

       缺点

    有时候对于小问题过于钻牛角尖

王璐瑶:

       优点:

              对字符串以及字符串数组的处理十分熟练

              任劳任怨

              很有想法,有好点子

       缺点:

              缺点:因生病而不在状态,没注意到比较细的地方,时间较少

 

十二、完成后实际的PSP

PSP

任务内容

实际完成需要的时间(min)

Planning

计划

0.5

Estimate

   估计这个任务需要多少时间,并规划大致工作步骤

0.5

Development

开发

53.25

   Analysis

需求分析 (包括学习新技术)

0.5

Design Spec

  生成设计文档

0.25

  Design Review

设计复审 (和同事审核设计文档)

0.25

Coding Standard

 代码规范 (为目前的开发制定合适的规范)

0.25

 Design

 具体设计

1

Coding

具体编码

40

Code Review

 代码复审

1

Test

测试(自我测试,修改代码,提交修改)

10

Reporting

报告

8

Test Report

测试报告

7

 Size Measurement

计算工作量

0.5

Postmortem & Process Improvement Plan

事后总结, 并提出过程改进计划

0.5

posted @ 2018-04-09 23:01  sfcard  阅读(319)  评论(0编辑  收藏  举报