四则运算 java实现
这个作业的要求来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2186。
我的github地址:https://github.com/destinymingyun/public
版本: V1.0
需求分析
由于开始只是上课听老师讲的根据《现在软件工程》中阿超给儿子的30道加减题,分析需求大致如下:
- 随机生成30道一个单运算符的题目
无流程分析,代码实现大致如下:
1 public class test { 2 public static void main(String[] args) { 3 Random random = new Random(); 4 int number1, number2, operational; 5 for(int i = 0; i < 30; i++) { 6 number1 = random.nextInt(); 7 number2 = random.nextInt(); 8 operational = random.nextInt(2); 9 10 switch (operational) { 11 case 0: 12 System.out.println(number1 + "+" + number2); 13 break; 14 case 1: 15 System.out.println(number1 + "-" + number2); 16 break; 17 default: 18 break; 19 } 20 } 21 } 22 }
用时15分钟
版本: V1.1
需求分析
根据老师发布的博客的具体要求:任何编程语言都可以,命令行程序接受一个数字输入,然后输出相应数目的四则运算题目和答案。例如输入数字是 30, 那就输出 30 道题目和答案。 运算式子必须有两个运算符,运算数字是在 100 之内的正整数,
答案不能是负数。 如:
23 - 3 * 4 = 11
根据老师发布的博客,需求定义如下:
- 加减法变成四则运算题目
- 两个运算符算式
- 运算数字是在 100 之内的正整数
- 输出相应题目的答案
需求变化
- 增加乘除运算符
- 增加运算符数量
- 限定运算数在100以内
- 计算结果并限定结果为正数
代码设计
主要根据V1.0的代码添加和修改得
1 public class test2 { 2 public static void main(String[] args) { 3 System.out.println("请输入题目个数:"); 4 Scanner input = new Scanner(System.in); 5 int number1, number2, operational; 6 int number3, operational2; 7 int num = input.nextInt(); 8 Random random = new Random(); 9 for(int i = 0; i < num; i++) { 10 int answer = 0; 11 number1 = random.nextInt(100); 12 number2 = random.nextInt(100); 13 number3 = random.nextInt(100); 14 operational = random.nextInt(4); 15 operational2 = random.nextInt(4); 16 String equation = null; 17 switch (operational) { 18 case 0: 19 switch(operational2) { 20 case 0: 21 answer = number1 + number2 + number3; 22 equation = number1 + "+" + number2 + "+" + number3 + "=" + answer + ""; 23 break; 24 case 1: 25 answer = number1 + number2 - number3; 26 equation = number1 + "+" + number2 + "-" + number3 + "=" + answer + ""; 27 break; 28 case 2: 29 answer = number1 + number2 * number3; 30 equation = number1 + "+" + number2 + "*" + number3 + "=" + answer + ""; 31 break; 32 case 3: 33 System.out.println("a"+answer); 34 equation = number1 + "+" + number2 + "/" + number3 + "=" + answer + ""; 35 break; 36 } 37 break; 38 case 1: 39 switch(operational2) { 40 case 0: 41 answer = number1 - number2 + number3; 42 equation = number1 + "-" + number2 + "+" + number3 + "=" + answer + ""; 43 break; 44 case 1: 45 answer = number1 - number2 - number3; 46 equation = number1 + "-" + number2 + "-" + number3 + "=" + answer + ""; 47 break; 48 case 2: 49 answer = number1 - number2 * number3; 50 equation = number1 + "-" + number2 + "*" + number3 + "=" + answer + ""; 51 break; 52 case 3: 53 answer = number1 - number2 / number3; 54 equation = number1 + "-" + number2 + "/" + number3 + "=" + answer + ""; 55 break; 56 } 57 break; 58 case 2: 59 switch(operational2) { 60 case 0: 61 answer = number1 * number2 + number3; 62 equation = number1 + "*" + number2 + "+" + number3 + "=" + answer + ""; 63 break; 64 case 1: 65 answer = number1 * number2 - number3; 66 equation = number1 + "*" + number2 + "-" + number3 + "=" + answer + ""; 67 break; 68 case 2: 69 answer = number1 * number2 * number3; 70 equation = number1 + "*" + number2 + "*" + number3 + "=" + answer + ""; 71 break; 72 case 3: 73 answer = number1 * number2 / number3; 74 equation = number1 + "*" + number2 + "/" + number3 + "=" + answer + ""; 75 break; 76 } 77 break; 78 case 3: 79 switch(operational2) { 80 case 0: 81 answer = number1 / number2 + number3; 82 equation = number1 + "/" + number2 + "+" + number3 + "=" + answer + ""; 83 break; 84 case 1: 85 answer = number1 / number2 - number3; 86 equation = number1 + "/" + number2 + "-" + number3 + "=" + answer + ""; 87 break; 88 case 2: 89 answer = number1 / number2 * number3; 90 equation = number1 + "/" + number2 + "*" + number3 + "=" + answer + ""; 91 break; 92 case 3: 93 answer = number1 / number2 / number3; 94 equation = number1 + "/" + number2 + "/" + number3 + "=" + answer + ""; 95 break; 96 } 97 break; 98 default: 99 break; 100 } 101 if(answer < 0) { 102 i--; 103 continue; 104 } 105 System.out.println(equation); 106 } 107 } 108 }
用时10分钟
在V1.0的基础上修改的V.1.1的版本。
满足了所有需求,几乎所有时间都是在具体编码上,虽然觉得switch那里明显不够好,但是对于我来说,无疑是最快的写法,应付作业还是很OK的。
版本: V1.2 V2.0
需求分析
根据老师发布的博客的具体要求:要求能出和真分数 (二分之一, 十二分之五,等)相关的练习题, 并且要求能处理用户的输入,并判断对错,打分统计。 要求能处理用户输入的真分数, 如 1/2, 5/12 等。
根据老师发布的博客,需求定义如下:
- 能处理真分数
- 用户能输入答案且能输入分数作为答案
- 能判断用户输入对错且打分
- 题目答案大于0
一开始本来打算直接在原来的V1.1上修改和添加完成V1.2,但是发现从V1.0上添加到V1.1上的方法来完成到V1.2几乎很难实现,感觉代码变得很乱,从V1.1升级到V1.2几乎所有位置都要有改动。
因为V1.1没有考虑到日后维护和升级。在艰难的选择下,我还是决定重写。
构思&设计
1.业务设计
由Main类负责用户所关心的输入输出的处理业务,将题库的相关业务托管给SubjectLibrary类,SubjectLibrary只关心要生成多少道题目和题目的答案以及一些限制的参数,然后返回一个List集合的题库,将运算符单独用一个Operation类表示,专门负责运算符的字符形式和整形表示的转化。
2.延展性设计
1.将SubjectLibrary类中生成3个数的生成和运算拓展为n个数的生成和运算。
2.在SubjectLibrary类中添加了是否允许题目重复的功能。
3.将题目答案>0拓展为可以设置题目答案的上下边界
3.健壮性设计
1.在SubjectLibrary的set方法中增加判断,不满足要求则抛出异常信息,保证SubjectLibrary类的健壮性
2.在用户输入时进行判断是否满足要求,否则要求重新输入,保证程序的交互性
主要代码
main类代码
1 public class Main { 2 public static void main(String[] args) { 3 int equationNum; 4 Scanner input = new Scanner(System.in); 5 6 do { 7 System.out.println("请输入题目个数"); 8 equationNum = input.nextInt(); 9 }while(equationNum < 0); 10 11 SubjectLibrary subjectLibrary = new SubjectLibrary(equationNum); // 传入要生成的题目数目 12 List<String> lib = subjectLibrary.getEquations(); // 获得题库 13 int count = 0; 14 String answer; 15 System.out.println("现在开始答题,除不尽用分数表示"); 16 for (String str: lib) { 17 System.out.print(str+"="); 18 answer = input.next(); 19 if (SubjectLibrary.calculate(answer).equals(SubjectLibrary.calculate(str)) ) { 20 System.out.println("答对了"); 21 count++; 22 } 23 else { 24 System.out.println("正确答案为:" + SubjectLibrary.calculate(str)); 25 } 26 } 27 int num = subjectLibrary.getEquationNum(); 28 System.out.println("共" + num + "题,答对" + count +"题"); 29 } 30 }
SubjectLibrary类成员变量和方法
Operation类代码
1 public class Operation { 2 public static final int equalitySign = 0, plus = 1, subtract = 2, multiplication = 3, division = 4; 3 4 public static String operationToString(int operation) { 5 switch (operation) { 6 case plus: 7 return "+"; 8 case subtract: 9 return "-"; 10 case multiplication: 11 return "*"; 12 case division: 13 return "/"; 14 case equalitySign: 15 return "="; 16 } 17 return null; 18 } 19 }
主要问题和心得
问题
一开始是想自己写一个多项式的运算的类,但是在试用正则表达式将运算符和运算数字切开时遇到运算符压入栈中开头出现空串问题,尝试多次解决无效后,只能曲线救国,通过调用javascript脚本的计算函数来解决。在类的设计上,本想将题库的生成和答案的计算多项式作为两个单独而互不影响的类,但是对于生成答案大于0的这个需求一度犹豫了很久,要将对于答案的判断这个业务放在题库的生成里,还是调用计算类的方法,最后还是将计算多项式类并到题库生成类中作为一个静态方法。对于用户的输入的安全性,不知道该放在题库生成中以异常的形式抛出,还是在输入时进行判断。最后两边都做了处理,但是却又感觉这样重复。
心得
参考了《Google Java 编程风格》虽然在写代码来效率低了很多,但是代码可读性大大增强,减少了大批量的注释,如其中说的这样一段话:
“在决定一个字段是否是一个常量时, 考虑它是否真的感觉像是一个常量。例如,如果任何一个该实例的观测状态是可变的,则它几乎肯定不会是一个常量。 只是永远不打算
改变对象一般是不够的,它要真的一直不变才能将它示为常量。”所以我在这次中定义了static final int plus = 1;确实比原来0,1这种可读性更强。
也参考了《设计模式之禅》,现在由于在初学阶段,虽然对很多设计原则不太了解,而且在类和函数设计上花费了大量时间,但是感觉代码质量比以前好很多。三次需求的变更也让我更用心去设计代码和考虑未来需求变更的延展性。
表
PSP2.1 | Personal Software Process Stages | 计划(分钟) | 实际(分钟) |
Planning | 计划 | 5 | 30 |
· Estimate | 估计这个任务需要多少时间 | 120 | 640 |
Development | 开发 | 120 | 300 |
· Analysis | 需求分析 (包括学习新技术) | 10 | 60 |
· Design Spec | 生成设计文档 | 0 | 0 |
· Design Review | 设计复审 | 20 | 40 |
· Coding Standard | 代码规范 | 0 | 30 |
· Design | 具体设计 | 30 | 120 |
· Coding | 具体编码 | 120 | 150 |
· Code Review | 代码复审 | 40 | 60 |
· Test | 测试(自我测试,修改代码,提交修改) | 60 | 20 |
Reporting | 报告 | 30 | 35 |
Test Report | 测试报告 | 0 | 0 |
·workload | 计算工作量 | 0 | 50 |
·correction | 并提出过程改进计划 | 0 | 95 |