zoukankan      html  css  js  c++  java
  • 四则运算结对项目之GUI

        本次结对编程让我学到了许多许多知识,受益匪浅!在此之前,我没想过我能做出一个双击运行的小程序。

        感谢我的队友与我同心协力,感谢室友宇欣告诉我操作符为“最多多少”而不是“多少”并教我使用效能分析工具,感谢陈杰不辞辛苦帮我测试14寸显示屏效果,感谢福孝大佬给我发的安装包!感谢学姐对项目的建议!

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

        本人:庄莉,学号:2016012034

        队友:王璐瑶,学号:2016012095


     

    计划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

     


     

    结对编程对接口的设计

    信息隐藏(Information Hiding)

        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.

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

        第一类信息是复杂化的信息。对于我们的项目,我们的main函数只有一个对gui的实例化,使用者并不知道内部的运行方式。内部的算法实现封装起来,外部只有调用的接口,只可以调用方法,不可以改变内部变量,做到了信息隐藏。

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

    接口设计(Interface Design)

        在本项目设计接口过程中,按需求新建接口,使用明确的命名方式使接口的功能清晰化,增强了可读性;接口与接口之间互相独立,使用方便。

    松耦合(Loose coupling)

        耦合的强度依赖于:(1)一个模块对另一个模块的调用;(2)一个模块向另一个模块传递的数据量;(3)一个模块施加到另一个模块的控制的多少;(4)模块之间接口的复杂程度。等等。

        模块内子程序(下一个层次上)应共享数据(有一定的耦合度),而减少全局变量能降低子程序性间的耦合性。

      类与类之间通常通过接口的契约实现服务提供者/服务请求者模式,这就是典型的松耦合。

        耦合程度越高,模块与模块之间的联系性就更高,系统的灵活性就越低,报错率就更高。在我们的项目中,计算模块的调用都比较单一,没有双向调用,使用之间互不干扰,增加了灵活性。

     


     

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

        经过商讨,我们决定基于我的个人项目修改。我先删除了原来的分数运算,在将普通四则运算与括号四则运算拆分,变成简单加减、四则运算、有括号加减与有括号四则运算。如图我分为5个类(test为单元测试)。

    • Command类:命令行测试类,负责接收命令行的参数并启动程序。
    • fileCreate类:创建文件类,负责产生result.text文件,将练习题写入文件以及做题模式的生成记录。
    • formula类:式子类,负责根据调用产生同种类型的式子,含有AddSubtract(加减运算)、arithmetic(简单四则运算)、Bracket(带括号的四则运算)、Bracket_AS (带括号的加减运算)四种函数。
    • calculate类:计算类,负责各种计算,含有有条件产生后一位数、有条件操作符等7个方法。
    • stack类:栈,负责计算式子,并判断式子合法性。

        其中,有条件生成操作符与后一位数我较为满意,它大大的降低了运行效率,部分代码可看第5模块的性能改进模块。

     


     

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

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

     1     // 计算结果
     2     public static Object result(String temp) {
     3         ScriptEngineManager sem = new ScriptEngineManager();
     4         ScriptEngine se = sem.getEngineByName("js");
     5         Object last = 0;
     6         try {
     7             last = se.eval(temp);
     8         } catch (ScriptException e) {
     9             e.printStackTrace();
    10         }
    11         return last;
    12     }
    result函数

        在栈的运算中加入判断

    1 if (Math.abs(sresulat) > upper || Math.abs(sresulat) < lower)
    2 {
    3     return 0;
    4 }
    判断

        而对于简单加减无括号全程不改变优先级的运算则不过栈,直接边生成数字便运算,减少了运算时间。

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

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

     1 // 相加不超过范围
     2     public static int decide0(int x, int min, int max)
     3     {
     4         int y;
     5         int temp = 0;
     6         if (x > 0)
     7         {
     8             temp = max - min - x + 1;// 加一个正整数范围
     9         } else
    10         {
    11             temp = max - (min - x) + 1;// 加至正整数的范围
    12         }
    13         if (temp < 0)
    14         {// 范围小于0
    15             if (x > 0)
    16             {
    17                 temp = Math.abs(x) - min * 2 + 1;// 正整数过大,需加负数
    18                 y = 0 - (int) (Math.random() * temp) - min;
    19             } else
    20             {
    21                 temp = Math.abs(x) - 2 * min + 1;// 负数过小,越值,加小整数至负数范围
    22                 y = (int) (Math.random() * temp) + min;
    23             }
    24         } else
    25         {
    26             y = (int) (Math.random() * temp + min);
    27         }
    28         return y;
    29     }
    30 
    31     // 相减不小于最小
    32     public static int decide1(int x, int min, int max)
    33     {
    34         int temp = 0;
    35         int y = 0;
    36         if (x > 0)
    37         {
    38             temp = x - 2 * (min - 1) - 1; // 减一个正数范围
    39         } else
    40         {
    41             temp = max + x - min + 1;// 减一个正数范围
    42         }
    43         if (temp > 0)
    44         {
    45             if (x < 0 && temp < min)
    46             {
    47                 temp = Math.abs(x) - 2 * min + 1;// 负数过小,需减负数
    48                 y = 0 - (int) (Math.random() * temp) - min;
    49             } else
    50             {
    51                 y = (int) (Math.random() * temp + min);
    52             }
    53         } else
    54         {
    55             temp = max - x - min + 1;// 只有x>0的情况会出现,正数过小,需减负数
    56             y = 0 - (int) (Math.random() * temp) - min;
    57         }
    58         return y;
    59     }
    加减法的后一位数选定

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

     1 // 操作符的选定
     2     public static int operator(int num, int middle2, int middle3)
     3     {
     4         if (Math.abs(num) <= middle2)
     5         {// 除法下界
     6             if (Math.abs(num) < middle3)
     7             {
     8                 return 3;
     9             } else
    10             {
    11                 return 0;
    12             }
    13         } else if (Math.abs(num) >= middle3)
    14         {// 乘法上界
    15             return 2;
    16         } else
    17         {
    18             return (int) (Math.random() * 4);
    19         }
    20     }
    21 // 下一位数字的选定
    22     public static int[] numberB(int key, int num, int lower, int upper)
    23     {
    24         int[] find = new int[] { 0, lower };
    25         if (key == 0)
    26         {
    27             find[1] = decide0(num, lower, upper);
    28             return find;
    29         } else if (key == 2)
    30         {
    31             int[] judge = new int[2];
    32             judge = decide2(num, lower);// 确保能够整除,并不低于下限
    33             if (judge[0] == 0)
    34             {
    35                 find[1] = judge[1];
    36                 return find;
    37             } else
    38             {
    39                 find[0] = 1;
    40             }
    41         } else if (key == 3)
    42         {
    43             find[1] = decide3(num, lower, upper);
    44             if (find[0] == 0)
    45             {
    46                 return find; // 乘法不超过上限
    47             }
    48         }
    49         find[1] = decide1(num, lower, upper);
    50         return find;
    51     }
    操作符选定以及下一位数字的选定

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

        在附加题记录用户模块,一开始使用contains(name)函数判断用户,后来发现这样会出现abc与abcabc被认为同一个人而的情况,经过思考,我们使用字符串的断开。

      1 String[] arrays = txt.split(" "); 

        再使用equals(String)函数判断用户,解决了这个问题。

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

     1 // 带括号的四则运算
     2     public static String Bracket(int lower, int upper, int o) {
     3         int middle2 = lower * lower;// 除法下界
     4         int middle3 = upper / lower;// 乘法上界
     5         int brack_left = 0; // 记录未匹配的左括号个数
     6         int brack = 0; // 括号个数
     7         int j = 0;
     8         char[] p = new char[] { '+', '-', '÷', '*' };
     9         String temp1 = "";
    10         int[] num = new int[o + 1]; // 数字
    11         int[] key = new int[o]; // 符号所在的下标
    12         num[0] = (int) (Math.random() * (upper - lower + 1) + lower);
    13         int result;
    14         int[] find = new int[2];
    15         for (j = 0; j < (o - 1); j++) {
    16             if (num[j] < 0) {
    17                 temp1 += "(" + String.valueOf(num[j]) + ")";
    18             } else {
    19                 temp1 += String.valueOf(num[j]);
    20             }
    21             int tmpcnt = brack_left;
    22             for (int i = 0; i < tmpcnt; i++) { // 若当前有未匹配的左括号,则对每一个未匹配的左括号,都有一定概率生成相应右括号。
    23                 if ((int) (Math.random() * 5) > 1) { // 生成右括号概率为0.6
    24                     brack_left--;
    25                     temp1 += ")";
    26                 }
    27             }
    28             key[j] = calculate.operator(num[j], middle2, middle3);
    29             find = calculate.numberB(key[j], num[j], lower, upper);
    30             if (find[0] == 1) {
    31                 key[j] = 1;
    32             }
    33             num[j + 1] = find[1];
    34             temp1 += String.valueOf(p[key[j]]);
    35             if (((brack * 2) <= o) && (((int) (Math.random() * 2)) == 0)) { // 以一定概率生成左括号,概率为1/2
    36                 temp1 += "(";
    37                 brack++;
    38                 brack_left++;
    39                 j++;
    40                 if (num[j] < 0) {
    41                     temp1 += "(" + String.valueOf(num[j]) + ")";
    42                 } else {
    43                     temp1 += String.valueOf(num[j]);
    44                 } // 生成左括号后必须生成一个数字和运算符,不然可能出现(15)这样的错误
    45                 key[j] = calculate.operator(num[j], middle2, middle3);
    46                 find = calculate.numberB(key[j], num[j], lower, upper);
    47                 if (find[0] == 1) {
    48                     key[j] = 1;
    49                 }
    50                 num[j + 1] = find[1];
    51                 temp1 += p[key[j]];
    52             }
    53         }
    54         while (j != o) { // 判断是否为最后一个数
    55             if (num[j] < 0) {
    56                 temp1 += "(" + String.valueOf(num[j]) + ")";
    57             } else {
    58                 temp1 += String.valueOf(num[j]);
    59             }
    60             key[j] = calculate.operator(num[j], middle2, middle3);
    61             temp1 += p[key[j]];
    62             find = calculate.numberB(key[j], num[j], lower, upper);
    63             if (find[0] == 1) {
    64                 key[j] = 1;
    65             }
    66             j++;
    67             num[j] = find[1];
    68         }
    69         if (num[o] < 0) {
    70             temp1 += "(" + String.valueOf(num[o]) + ")";
    71         } else {
    72             temp1 += String.valueOf(num[o]);
    73         }
    74         while ((brack_left) != 0) { // 补全右括号
    75             temp1 += ")";
    76             brack_left--;
    77         }
    78         result = stack.work(temp1, lower, upper, 1);
    79         if (result == 0) {
    80             temp1 = Bracket(lower, upper, o);
    81         }
    82         return temp1;
    83 
    84     }
    85 
    86 }
    有括号四则运算

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

         性能分析过程截图:

         按F4,出现以下截图。资源全部被回收。证明没有资源泄露。程序性能良好。

          使用单元测试的CPU分析如下图:

        使用Command.java的CPU效能分析如下图: 


     

    单元测试

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

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

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

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

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

     1 @Test
     2     public void testAll() {
     3         // 顺序不同以及异常测试。生成的文件会被覆盖。
     4         String[] arg0 = new String[] { "-n", "100", "-m", "5", "100", "-o", "3", "-c", "-b" };
     5         String[] arg1 = new String[] { "-m", "5", "50", "-o", "3", "-n", "100", "-c" };
     6         String[] arg2 = new String[] { "-o", "3", "-m", "5", "50", "-n", "100", "-b" };
     7         String[] arg3 = new String[] { "-n", "100", "-o", "3", "-m", "5", "50" }; 
     8         Command.main(arg0);// 有括号四则运算测试
     9         Command.main(arg1);// 四则运算测试
    10         Command.main(arg2);// 有括号加减运算测试
    11         Command.main(arg3);// 加减运算测试
    12 }
    命令行正确输入测试

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

        命令行单元测试覆盖率截图如下:

    1 @Test
    2     public void testDecide2() {
    3         int[] find = new int[2];
    4         find = calculate.decide2(20, 2);
    5         assertEquals(2, find[1]);
    6         find = calculate.decide2(13, 2);
    7         assertEquals(1, find[0]);
    8     }
    除法选择除数测试

        decide2(int x, int min)为除法选择除数的函数,函数如下:

     1 // 被除数能被除数整除并不低于最小
     2     public static int[] decide2(int x, int min)
     3     {
     4         int[] judge = new int[] { 1, 0 };
     5         int temp = Math.abs(x) / min - min + 1;// 除数的范围
     6         for (int i = min; i < (temp + min); i++)
     7         {
     8             if (Math.abs(x) % i == 0)
     9             {// 判断是否整除
    10                 judge[0] = 0;
    11                 judge[1] = i;
    12                 return judge;
    13             }
    14         }
    15         return judge;
    16     }
    decide2函数

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


    异常说明

     1 @Test
     2     public void testAll() {
     3         String[] arg4 = new String[] { "-o", "3", "-m", "5", "50", "-n" };
     4         String[] arg4_1 = new String[] { "-o", "3", "-n", "-m", "5", "50" };
     5         String[] arg4_2 = new String[] { "-n", "100000", "-m", "5", "50" };
     6         String[] arg4_3 = new String[] { "-o", "3", "-m", "5", "50" };
     7 
     8         String[] arg5 = new String[] { "-n", "50" };
     9         String[] arg5_1 = new String[] { "-m", "5", "-n", "50", "-o", "3" };
    10         String[] arg5_2 = new String[] { "-n", "50", "-m", "3" };
    11         String[] arg5_3 = new String[] { "-n", "50", "-o", "3", "-m" };
    12         String[] arg5_4 = new String[] { "-m", "-n", "50" };
    13 
    14         String[] arg6 = new String[] { "-o", "11", "-m", "5", "50", "-n", "100" };
    15         String[] arg6_1 = new String[] { "-n", "100", "-o", "-m", "5", "50" };
    16         String[] arg6_2 = new String[] { "-n", "100", "-m", "5", "50", "-o" };
    17 
    18         String[] arg7 = new String[] { "-m", "5", "20", "-n", "100", "-c" };
    19         String[] arg7_1 = new String[] { "-m", "5", "50", "-n", "100", "-b" };
    20 
    21         String[] arg8 = new String[] { "-b", "1", "-o", "3", "-m", "5", "50", "-n", "100" };
    22         String[] arg8_1 = new String[] { "-c", "1", "-o", "3", "-m", "5", "50", "-n", "100" };
    23         String[] arg8_2 = new String[] { "-n", "100", "-m", "5", "50", "-d" };
    24 
    25         Command.main(arg4);// 缺少题数值测试
    26         Command.main(arg4_1);
    27         Command.main(arg4_2);// 题数值过大测试
    28         Command.main(arg4_3);// 缺少题数测试
    29 
    30         Command.main(arg5);// 缺少数值范围
    31         Command.main(arg5_1);// 缺少数值范围上限测试
    32         Command.main(arg5_2);
    33         Command.main(arg5_3);// 缺少数值范围上下限测试
    34         Command.main(arg5_4);
    35 
    36         Command.main(arg6);// 操作符数值过大测试
    37         Command.main(arg6_1);// 缺少操作符数值测试
    38         Command.main(arg6_2);
    39 
    40         Command.main(arg7);// 乘除需要上界大于下界的平方
    41         Command.main(arg7_1);// 括号需要操作符数大于1
    42 
    43         Command.main(arg8);// 输入非法测试之b后有数字
    44         Command.main(arg8_1);// 输入非法测试之c后有数字
    45         Command.main(arg8_2);// 输入非法测试之无辨识字符
    46     }
    命令行异常输入测试

        对于命令行可能出现的异常大概有13个:

    • 缺少题数值(-n后无带数字,如arg4与arg4_1)时,提醒缺少题数值,并告知-n的范围;

    • 题数值过大(-n后数值超过10000,如arg4_2)时,提醒告知题数值范围(过小同理);

    • 缺少题数(命令中无-n,如arg4_3)时,提醒-n为必须项,并告知-n范围。

    • 缺少数值范围(命令中无-m,如arg5)时,提醒-m为必须项,并告知-m上下限各自范围;

    • 缺少数值范围上限(-m后只带一个数字,如arg5_1和 arg5_2)时,提醒缺少上限,并告知上限范围;

    • 缺少数值范围上下限(-m后不带数字,如arg5_3和 arg5_4)时,提醒缺少上下限,并告知上下限各自范围;

    • 数值范围数值过小过大时,提醒告知操作符数值范围。

    • 操作符数值过大(-o后数值超过10,如arg6)时,提醒告知操作符数值范围(过小同理);

    • 缺少操作符数值(输入-o,后方没有带数值,如arg6_1与arg6_2)时,提醒缺少操作符数值,并告知-o范围。

    • 选择乘除法但是上界小于下界的平方,无法生成含有乘除的式子(如arg7)时,提醒上界需大于下界的平方;

    • 选择括号但是操作符默认为1或选择为1,不符合生成括号的条件(如arg7_1)时,提醒选择括号需要操作符数大于1。

    • –b(或-c)后带数字(如arg8与arg8_1),提醒-b(或-c)后不能带数字;

    • 出现除m、n、o、b、c外的字符如d等(如arg8_2),提醒输入值非法。

     

     


    界面模块的详细设计过程

        设计图如下:

        我们先从选择出题或做题开始。

        选择出题则进入出题参数输入界面。

        利用MouseListenermouseEntered(MouseEvent e)setTitle(String);使得鼠标移到参数上,标题会有提示功能。

        输入完毕点击确认后,由输入的参数判断是否有异常并提示直至无异常创建文件。

     1 public class submitListener implements ActionListener {
     2         public void actionPerformed(ActionEvent e) {
     3             String m = "题数与数值上下限为必填项,请按标题提示输入正整数!";
     4             String m2 = "创建文件成功!";
     5             int n0, lower0, upper0, o0, c0, b0;
     6             o0 = 1;
     7             c0 = 0;
     8             b0 = 0;
     9             String o1 = "";
    10             try {
    11                 n0 = Integer.parseInt(n.getText());
    12                 lower0 = Integer.parseInt(lower.getText());
    13                 upper0 = Integer.parseInt(upper.getText());
    14                 if (n0 < 1 || n0 > 10000) {
    15                     JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "题数范围为1-10000", "提示",
    16                             JOptionPane.INFORMATION_MESSAGE);
    17                     return;
    18                 }
    19                 if (lower0 < 1 || lower0 > 100) {
    20                     JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "下界范围为1-100", "提示",
    21                             JOptionPane.INFORMATION_MESSAGE);
    22                     return;
    23                 }
    24                 if (upper0 < 50 || upper0 > 1000) {
    25                     JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "上界范围为50-1000", "提示",
    26                             JOptionPane.INFORMATION_MESSAGE);
    27                     return;
    28                 }
    29                 if (upper0 < (2 * lower0)) {
    30                     JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "上界必须大于两倍下界", "提示",
    31                             JOptionPane.INFORMATION_MESSAGE);
    32                     return;
    33                 }
    34             } catch (NumberFormatException e2) {
    35                 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), m, "提示", JOptionPane.INFORMATION_MESSAGE);
    36                 return;
    37             }
    38             try {
    39                 o1 = o.getText();
    40                 o0 = Integer.parseInt(o1);
    41             } catch (NumberFormatException e2) {
    42                 if (!o1.equals("")) {
    43                     JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "请输入1-10的正整数或不输入保持默认,默认为1", "提示",
    44                             JOptionPane.INFORMATION_MESSAGE);
    45                     return;
    46                 }
    47             }
    48             if (c.isSelected()) {
    49                 c0 = 1;
    50             }
    51             if (b.isSelected()) {
    52                 b0 = 1;
    53             }
    54             if (o0 == 1 && b0 == 1) {
    55                 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "括号需要操作符数量大于1", "提示",
    56                         JOptionPane.INFORMATION_MESSAGE);
    57                 return;
    58             }
    59             if (c0 == 1 && upper0 < (lower0 * lower0)) {
    60                 JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "乘除法需要上界数值大于下界的平方", "提示",
    61                         JOptionPane.INFORMATION_MESSAGE);
    62                 return;
    63             }
    64             createFile.fileCreate(n0, lower0, upper0, o0, c0, b0);
    65             JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), m2, "提示", JOptionPane.INFORMATION_MESSAGE);
    66             System.exit(0);
    67         }
    68     }
    参数确认

        选择做题则先输入做题人名字(在这里建议使用英文,中文名字无法很好的记录)。

        接着上传文件,在这里使用了txt文件过滤器,使之仅可上传txt文件。

    1 FileFilter filter = new FileNameExtensionFilter("Text file", "txt");
    2 JFileChooser fileChooser = new JFileChooser();
    3 fileChooser.setAcceptAllFileFilterUsed(false);
    4 fileChooser.addChoosableFileFilter(filter);
    5 FileSystemView fsv = FileSystemView.getFileSystemView();
    过滤器

        另外,出题与做题都统一为utf-8编码,免去执行文件编码错误。

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8"));
    InputStreamReader read = new InputStreamReader(new FileInputStream(file), "utf-8");

        上传成功后开始计时做题,并于最后结果中显示用时。

        经过后面JTextPane控件的启发,我考虑到出题时题目长度有长短,为了更加美观显示,应该需要自动换行,我同样采用了HTML编辑文本的想法,做出改进。

     1 public static void JlabelSetText(JLabel jLabel, String longString) throws InterruptedException {
     2         StringBuilder builder = new StringBuilder("<html>");
     3         char[] chars = longString.toCharArray();
     4         FontMetrics fontMetrics = jLabel.getFontMetrics(jLabel.getFont());
     5         int start = 0;
     6         int len = 0;
     7         while (start + len < longString.length()) {
     8             while (true) {
     9                 len++;
    10                 if (start + len > longString.length())
    11                     break;
    12                 if (fontMetrics.charsWidth(chars, start, len) > jLabel.getWidth()) {
    13                     break;
    14                 }
    15             }
    16             builder.append(chars, start, len - 1).append("<br/>");
    17             start = start + len - 1;
    18             len = 0;
    19         }
    20         builder.append(chars, start, longString.length() - start);
    21         builder.append("</html>");
    22         jLabel.setText(builder.toString());
    23 }
    换行

        计时方面我原本采用秒数计时,后来考虑到当做题时间较长时,秒数很难清晰明确的表达,所以改用了 hh:mm:ss 法显示。

     1 public static String getTimeStrBySecond(long second) {
     2         if (second <= 0) {
     3             return "00:00:00";
     4         }
     5         int hours = (int) second / HOUR_SECOND;
     6         if (hours > 0) {
     7 
     8             second -= hours * HOUR_SECOND;
     9         }
    10         int minutes = (int) second / MINUTE_SECOND;
    11         if (minutes > 0) {
    12             second -= minutes * MINUTE_SECOND;
    13         }
    14         return (hours > 10 ? (hours + "")
    15                 : ("0" + hours) + ":" + (minutes > 10 ? (minutes + "") : ("0" + minutes)) + ":"
    16                         + (second > 10 ? (second + "") : ("0" + second)));
    17 }
    秒数转换

        自动换行处理与秒数转换被我写入新类——dataDeal类中。

        最终做完题目后除了显示用时,还显示题数、分数、错题以及该题正确答案,非首次用户会显示历史分数以及最高分数。

        原本该部分使用了JTextArea控件,但学姐建议正确答案部分对齐显示会更加美观,并提出了C#中的ListView控件,但很遗憾,Java中似乎并没有。JTextArea控件是纯文本显示,很难做到不同的对齐方式,所以我删除了该类。经过多方学习比较,我最终选择了JTextPane控件,该控件简单易用,可将文本显示为HTML文本,大大提高了编辑的样式性。我最终采取了表格法对齐,另外对重点突出的地方加粗变红显示,达到强调与一定视觉冲击效果,可从后文看到对比图。

     1 String text = "<p style='font-family:楷体; font-size:19'>" + name + " 本次用时<span style='color:red'><strong> "
     2                 + dataDeal.getTimeStrBySecond(spentTime) + " </strong></span>,得分<span style='color:red'><strong> "
     3                 + goal + " </strong></span>分。<br>";
     4         if (size0 == 0) {
     5             text += "你总共答了<span style='color:red'><strong> " + size
     6                     + " </strong></span>道题,并全部答对!<span style='color:red'><strong>恭喜!</strong></span></p>";
     7         } else {
     8             text += "你总共答了<span style='color:red'><strong> " + size
     9                     + " </strong></span>道题,答对<span style='color:red'><strong> " + size1
    10                     + " </strong></span>道,答错<span style='color:red'><strong> " + size0
    11                     + " </strong></span>道,分别为:</p><p><table border=0>";
    12             for (int i = 0; i < (size0 * 2); i++) {
    13                 text += "<tr><td style='font-family:楷体; font-size:19'><strong>" + wrong.get(i++)
    14                         + "   </strong></td><td width='180' style='font-family:楷体; font-size:19;color:red'><strong> "
    15                         + wrong.get(i) + "</strong></td></tr>";
    16             }
    17         }
    18         text += "</table></p>";
    19         text += "<p style='font-family:楷体; font-size:19'>" + createFile.record(name, goal) + "</p>";
    20 
    21         JTextPane textarea = new JTextPane();
    22         textarea.setContentType("text/html");
    23         textarea.setText(text);
    24         textarea.setEditable(false);
    25         JScrollPane textAreascrollPane = new JScrollPane(textarea);
    26         add(textAreascrollPane, BorderLayout.CENTER);
    JTextPane

     


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

        如图所示

        在界面模块选择出题输入参数之后调用fileCreate类,再由fileCreate类调用计算模块,创建result.txt

        在界面模块选择做题输入名字、上传文件、做题。做题时调用计算模块的stack类计算判断正确性,记录错题。最终结果由计算模块中的fileCreate类的record(String name, int goal)记录,由界面模块显示。

        实现的功能大致有12个,并且为了提高用户体验,修改了图标并增加了背景,将操作符数修改为下拉框选择,默认选择为1,避免输入非数字错误:

    模式选择

    出题参数输入(前后对比图)

    出题参数要求提醒

    输入参数有误提醒(见第七点异常)

    生成文件

    记录用户

    上传文件(只允许txt文件)

    判断文件是否为空或非练习题

    计时

    一道一道做题并且题目过长时自动换行

    评分

        根据学姐给的建议做出了修改,以下为前后对比图,正确答案对齐,使之更加美观。另外我修改了做题时间的显示形式,这样当做题时间较长时可以更加清晰的看出时间情况。而做题时间、得分情况、错题与正确答案皆加粗甚至标红,使之更加显眼,提高用户体验。

    记录历史分数与最高分数

     


    结对编程

        我们先一起分析了需求与功能的实现,并提出了一些有实质性的方法,并确认数据的传递方式。再分析各自的个人项目代码,指出了双方优劣性,在综合考虑选择基础代码加以改进。

        我们根据自己较为擅长的方面分工,如相对之下,我对gui较为熟悉,而她对字符串处理较为熟悉,则我负责界面展示而她负责命令行的分析。各自写完之后我们再复审双方代码,对代码不理解之处询问并补充注释,以及对双方异常情况补充。最后在一起整合双方代码,使之成为完整项目。


    结对编程的优缺点

        在此过程中我们互相帮助、互相学习、能力上得到互补,而代码和产品质量提高,有效地减少bug并且考虑到更多方面的情况。有两台电脑可以测试程序效果,如她的电脑比我小,我的gui显示不同,她的部分算式被遮挡,最终我选择了将按钮部分的面板设为透明,解决了这个问题。

        不足之处在于队友之间的进度相互影响,不同的代码风格之间的磨合也花费了一定时间。

        双方优缺点:

      庄莉 王璐瑶
    优点

    认真细心,有责任心

    任劳任怨

    代码能力高

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

    动手能力强

    很有想法,有好点子

    缺点

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

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

     

    实际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

    报告

    9

    Test Report

    测试报告

    8

     Size Measurement

    计算工作量

    0.5

    Postmortem & Process Improvement Plan

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

    0.5


        本次结对编程真的让我学到很多知识,尤其是各种操作,就像上一篇博客的链接一样,我查了许许多多这样的链接,学习了一种有一种的方法,与队友配合,完成了这次项目。而每次写博客,都能重新总结我的思路,受益良多。

        虽然真的很辛苦,但能做出来也就够了。


        以下部分由于时间与精力关系,我们小组并没有完成,仅提供思路参考,有想法的同学可加以尝试。

        附加题多语言思路参考:程序国际化

    https://blog.csdn.net/zhuxinquan61/article/details/51540806

     

    https://blog.csdn.net/zhoudaxia/article/details/37536195

        思路注意点参考:

    • 文字描述使用短语、名词与阿拉伯数字,便于翻译。 标准语言建议英语,中文字符难以使用字符串判断内容。
    • 语言选择可为下拉框列表旁边带一不可编辑的文本框。由于可以自添加,所以可以建立一个文件(如txt),文件中加入选项,执行代码时读出文件,for循环将每一项添加进下拉框列表中。有一项设置为其他或自定义,选择它时,旁边的文本框变为可编辑,用于添加语言(也可让下拉框为可编辑,当输入的语言下拉选项中没有时认为添加新语言)。
    • 添加语言则需配置文件(参考网址),将所有需要翻译的文字让用户对应翻译。生成新的配置文件,并命名为该语言(使用英文命名),将该语言添加到保存下拉框选项的文件中。
    • 当用户选择一种语言时,通过其相同的命名,即可调用该语言的配置文件进行翻译,达到多语言转化功能。
  • 相关阅读:
    第四次作业—四则运算
    第四次作业—代码规范
    【欢迎来怼】事后诸葛亮会议
    软件工程——第七次作业
    第17次Scrum会议(10/29)【欢迎来怼】
    软件工程——第六次作业
    第10次Scrum会议(10/22)【欢迎来怼】
    软件工程——第五次作业
    欢迎来怼——第四次Scrum会议
    软件工程——第四次作业(3)
  • 原文地址:https://www.cnblogs.com/maomao-miao/p/8766895.html
Copyright © 2011-2022 走看看