这是一系列模块化文章的开端。
一切将从一份 JAVA 代码开始。这份代码实现了一个能自动生成小学四则运算题目的命令行 “软件”,满足以下需求:
- 除了整数以外,还支持真分数的四则运算。例如:1/6 + 1/8 = 7/24
- 运算符为 +, −, ×, ÷
- 能处理用户的输入(整数、小数、真分数)
- 对用户的输入判断对错,并统计正确率
虽然实现了需求,但是所有的代码都写在 main() 方法里面。
这一系列的文章演示了将其一步步模块化的过程。
初始的代码如下:
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.math.RoundingMode;import java.text.DecimalFormat;import java.util.Scanner;public class lastdemo { private static int Gcd(int num1, int num2) {// 求最大公约数 num1 = Math.abs(num1);// 负数取绝对值 num2 = Math.abs(num2); int min = Math.min(num1, num2); int maxSubmultiple = 1; for (int i = min; i >= 1; i--) { if (num1 % i == 0 && num2 % i == 0) { maxSubmultiple = i; break; } } return maxSubmultiple; } public static void main(String[] args) { double count1 = 0; BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); Scanner in1 = new Scanner(System.in); System.out.print("请输入你想要作答的题目数量:"); int N = in1.nextInt(); System.out.print("请选择你要进行的运算:1.整数;2.真分数;"); /* Scanner in3 = new Scanner(System.in); */ int input1 = in1.nextInt(); if (input1 == 1) { for (int i = 1; i <= N; i++) { int a = (int) (Math.random() * 10 + 1);/* 防止出现不好处理的0,很不严谨不可取 */ int b = (int) (Math.random() * 10 + 1); int c = (int) (Math.random() * 4); int result = 0; switch (c) { case 0: System.out.print("第" + i + "题" + ": "); System.out.print(a + " + " + b + " = "); result = a + b; break; case 1: if (a < b) { int t = a; a = b; b = t; } System.out.print("第" + i + "题" + ": "); System.out.print(a + " - " + b + " = "); result = a - b; break; case 2: System.out.print("第" + i + "题" + ": "); System.out.print(a + " × " + b + " = "); result = a * b; break; case 3: System.out.print("第" + i + "题" + ": "); if (a < b) { int t = a; a = b; b = t; } if (a % b != 0) { a = (int) (Math.random() * 10 + 1) * b;/* 保证能整除 */ } System.out.print(a + " ÷ " + b + " = "); result = a / b; break; } Scanner in2 = new Scanner(System.in); int input = in2.nextInt(); if (input == result) { count1++; System.out.println("答对了,恭喜\n"); } else { System.out.println("答错了,加油\n"); } } System.out.println("True Rate:" + count1 / N); } else if (input1 == 2) { int max = 100;// 控制算式个数 char[] op = { ' ', '+', '-', '*', '÷' };// 操作符 int[] No = new int[4];// 操作符地址 int useNo = 0;// 控制操作符 int[] num1 = new int[10];// 操作数 int[] numberArea = { 1, 10 };// 数值范围 String[] Useranser = new String[max];// 用户输入的答案 String[] StandardAnswer = new String[max];// 标准答案 int f = 0;// 控制输出真分数的操作符 int count = 0;// 统计答题正确的数量 DecimalFormat decimal = new DecimalFormat("#.##"); decimal.setRoundingMode(RoundingMode.HALF_UP); int fz1 = 1;// 分子通分 int fz2 = 1;// 分子通分 int fm2 = 1;// 分母通分 int result = 0;// 分子计算 int gcd;// 最大公约数 int fzZeromark = 0;// 分数除法,分子为0标志位 int fzZeroMark = 0;// String zjfz = new String();// 最简分子 String zjfm = new String();// 最简分母 System.out.print("请选择是否需要乘除法算术题(Y or N):");// 是否进行乘除法 char OPP;// 控制是否需要乘除法 Scanner in2 = new Scanner(System.in); OPP = in2.next().charAt(0); if (OPP == 'Y' || OPP == 'y') { useNo = 4; } else if (OPP == 'N' || OPP == 'n') { useNo = 2; } System.out.println("请计算下列真分数计算题。(若无法运算请填入null)"); System.out.println("***************************************"); for (int i = 0; i < N; i++) { System.out.print("(" + (i + 1) + ") "); for (int j = 0; j < 2; j++)// (第一个真分数) { num1[j] = (int) (Math.random() * (numberArea[1] - numberArea[0]) + numberArea[0]);// 控制随机数数值 if (j == 1) { while (num1[j - 1] > num1[j] || num1[j] == 0) { num1[j] = (int) (Math.random() * (numberArea[1] - numberArea[0]) + numberArea[0]);// 控制随机数数值 } } } for (int j = 2; j < 4; j++)// (第二个真分数) { num1[j] = (int) (Math.random() * (numberArea[1] - numberArea[0]) + numberArea[0]);// 控制随机数数值 if (j == 3) { while (num1[j - 1] > num1[j] || num1[j] == 0) { num1[j] = (int) (Math.random() * (numberArea[1] - numberArea[0]) + numberArea[0]);// 控制随机数数值 } } } for (int k = 0; k < 2; k++) {// 符号个数 No[k] = (int) (Math.random() * useNo + 1);// 随机产生操作符 } for (int h = 0; h < 4; h++) {// 2个真分数 if (h % 2 == 0) System.out.print(("(" + num1[h] + "/")); else if (h % 2 == 1) { System.out.print(num1[h] + ")"); if (f < 1) {// 控制只输出一个操作符 System.out.print(op[No[f]]); f++; } else System.out.println("="); } } f = 0; count = 0; for (int g = 0; g < 1; g++) { fz1 = num1[0] * num1[3]; fz2 = num1[1] * num1[2]; fm2 = num1[1] * num1[3];// 分母 fzZeromark = 0; fzZeroMark = 0; switch (op[No[g]]) { case '+': result = fz1 + fz2;// 分子 gcd = Gcd(result, fm2);// 除以公约数得到最简分数 zjfz = String.valueOf(result / gcd); zjfm = String.valueOf(fm2 / gcd); break; case '-': result = fz1 - fz2; gcd = Gcd(result, fm2); zjfz = String.valueOf(result / gcd); zjfm = String.valueOf(fm2 / gcd); break; case '*': result = num1[0] * num1[2]; gcd = Gcd(result, fm2); if (num1[0] == 0 || num1[2] == 0) { fzZeroMark = 1; } zjfz = String.valueOf(result / gcd); zjfm = String.valueOf(fm2 / gcd); break; case '/':// 乘以另一个数的倒数 result = num1[0] * num1[3]; fm2 = num1[1] * num1[2]; gcd = Gcd(result, fm2); if (num1[0] == 0 || num1[2] == 0) { fzZeromark = 1; } zjfz = String.valueOf(result / gcd); zjfm = String.valueOf(fm2 / gcd); break; } } if (fzZeromark == 1) { StandardAnswer[i] = "null";// 当第二个数的分子为零时无法进行除法运算 } else if (fzZeroMark == 1) { StandardAnswer[i] = "0"; } else if (zjfz == zjfm) { StandardAnswer[i] = "1"; } else if (zjfm.equalsIgnoreCase("1")) { StandardAnswer[i] = zjfz; } else { StandardAnswer[i] = zjfz + "/" + zjfm; } } // 答题模板 System.out.println("请输入你的答案:"); for (int i = 0; i < N; i++) { System.out.print((i + 1) + ":"); Scanner in3 = new Scanner(System.in); Useranser[i] = in3.next(); if (Useranser[i].equals(StandardAnswer[i])) { count++; } } System.out.println("标准答案是 : "); for (int i = 0; i < N; i++) { System.out.println((i + 1) + ":" + StandardAnswer[i]); } System.out.println("True rate:" + String.valueOf(decimal .format(((float) count / (float) N) * 100)) + "%"); System.out.println("**************************************"); } else { System.out.println("请输入1或2:"); } if (null != in) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } }}
模块化结束后的版本:
模块化前后的文件图
最终类图
一些中间步骤的commit
id | time | commit message | tag |
---|---|---|---|
26daf92 | 03-08 16:03 | 初始版本 | (tag: 从网上获取的最初版本的代码) |
b427867 | 03-08 20:10 | 完成变量名的替换 | (tag: 完成变量名的替换) |
90e4368 | 03-08 20:29 | 调整结构并标记模块 | |
1cd9230 | 03-08 20:37 | 第一层模块化完毕 | |
d29caed | 03-08 21:14 | 分数加减乘除分支转发函数 | |
7cf2d6b | 03-08 21:20 | 提取出获取标准答案的部分 | |
65a6155 | 03-08 21:50 | 提取出随机获取分数的部分 | |
e6541d4 | 03-08 22:11 | 提取出分数的输出和随机符号 | |
0e75eef | 03-08 23:24 | 提取出整数加减乘除;合并整数和分数对于用户输入的处理方式 | (tag: 模块提取基本完成) |
a03cae2 | 03-09 11:35 | 根据类图创建类 | (tag: 根据类图创建初始类) |
1b67b32 | 03-09 11:53 | 整数算式生成放到ExpressionGenerator类里面 | |
7e63cef | 03-09 11:54 | 整数计算放到MyNum里面 | |
508f063 | 03-09 12:29 | 完成Expression的实现 | |
eb6ef0d | 03-09 14:02 | 添加对Expression为整数表达式的测试;从测试中发现问题并修复 | |
14c3215 | 03-09 14:39 | 简化LastDemo类的整数部分(程序暂时不能正确运行) | |
7b61b1c | 03-09 15:22 | 简化LastDemo类的分数部分 | |
6f8e171 | 03-09 15:31 | 实现答案的处理;实现分数表达式的生成 | |
68f585f | 03-09 15:44 | 简化getExpressionStr()的代码 | |
bacfb8f | 03-09 15:57 | 抽取读用户输入、输出答案、输出正确率 | |
3c0bcb7 | 03-09 16:38 | 替换加减乘除符号;生成两数相减式子时,保证结果为正 | |
31317c0 | 03-09 17:02 | 修复部分分数除法答案错误的问题;模块化完成; | (tag: 模块化完成) |