四则运算结对项目之GUI

2023-11-21 14:59
文章标签 项目 gui 四则运算 结对

本文主要是介绍四则运算结对项目之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循环将每一项添加进下拉框列表中。有一项设置为其他或自定义,选择它时,旁边的文本框变为可编辑,用于添加语言(也可让下拉框为可编辑,当输入的语言下拉选项中没有时认为添加新语言)。
  • 添加语言则需配置文件(参考网址),将所有需要翻译的文字让用户对应翻译。生成新的配置文件,并命名为该语言(使用英文命名),将该语言添加到保存下拉框选项的文件中。
  • 当用户选择一种语言时,通过其相同的命名,即可调用该语言的配置文件进行翻译,达到多语言转化功能。

 

转载于:https://www.cnblogs.com/maomao-miao/p/8766895.html

这篇关于四则运算结对项目之GUI的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/403091

相关文章

javafx 如何将项目打包为 Windows 的可执行文件exe

《javafx如何将项目打包为Windows的可执行文件exe》文章介绍了三种将JavaFX项目打包为.exe文件的方法:方法1使用jpackage(适用于JDK14及以上版本),方法2使用La... 目录方法 1:使用 jpackage(适用于 JDK 14 及更高版本)方法 2:使用 Launch4j(

Docker集成CI/CD的项目实践

《Docker集成CI/CD的项目实践》本文主要介绍了Docker集成CI/CD的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、引言1.1 什么是 CI/CD?1.2 docker 在 CI/CD 中的作用二、Docke

SpringBoot项目引入token设置方式

《SpringBoot项目引入token设置方式》本文详细介绍了JWT(JSONWebToken)的基本概念、结构、应用场景以及工作原理,通过动手实践,展示了如何在SpringBoot项目中实现JWT... 目录一. 先了解熟悉JWT(jsON Web Token)1. JSON Web Token是什么鬼

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

Jenkins中自动化部署Spring Boot项目的全过程

《Jenkins中自动化部署SpringBoot项目的全过程》:本文主要介绍如何使用Jenkins从Git仓库拉取SpringBoot项目并进行自动化部署,通过配置Jenkins任务,实现项目的... 目录准备工作启动 Jenkins配置 Jenkins创建及配置任务源码管理构建触发器构建构建后操作构建任务

Nginx、Tomcat等项目部署问题以及解决流程

《Nginx、Tomcat等项目部署问题以及解决流程》本文总结了项目部署中常见的four类问题及其解决方法:Nginx未按预期显示结果、端口未开启、日志分析的重要性以及开发环境与生产环境运行结果不一致... 目录前言1. Nginx部署后未按预期显示结果1.1 查看Nginx的启动情况1.2 解决启动失败的

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多